1、Collection接口概述

Collection接口是Java集合里的两大根接口之一,且Collection接口仅继承了Iterable接口。Java将存放一系列元素(非k-v值)的数据结构应具备的通用方法提炼出来,在公共接口Collection里声明这些方法,供不同的集合类去实现Collection接口里的这些方法。
Collection接口的通用方法如下:

  1. package java.util;
  2. import java.util.function.Predicate;
  3. import java.util.stream.Stream;
  4. import java.util.stream.StreamSupport;
  5. public interface Collection<E> extends Iterable<E> {
  6. /**
  7. *返回集合大小,也就是集合中元素的数量
  8. */
  9. int size();
  10. /**
  11. * 如果集合不包含元素则返回true,即判断集合是否为空
  12. */
  13. boolean isEmpty();
  14. /**
  15. * 判断集合是否存在某个对象,注意这里参数是个Object,并没有限制为E或其子类
  16. */
  17. boolean contains(Object o);
  18. /**
  19. * 返回一个迭代器iterator。并没有说明元素的迭代顺序,除非特别的集合有这个要求。
  20. */
  21. Iterator<E> iterator();
  22. /**
  23. *将集合转为对象数组,注意这里不是元素数组而是一个Object数组。
  24. *如果集合保证是有序的,那么通过迭代器返回数组有相同顺序
  25. *返回的数组是安全的,也就是说集合有自己的引用,数组开辟新的堆内存,也有自己的引用。所以调
  26. *用者可以随意操作返回的数组。
  27. *这个方法是数组和列表之间的桥梁
  28. */
  29. Object[] toArray();
  30. /**
  31. * 返回一个集合元素类型的数组。如果集合满足指定的数组并且有足够的空间,则在其中返回此集合
  32. * 否则返回此集合大小的新数组。
  33. * 如果集合有序,那么返回此集合迭代器遍历顺序的数组
  34. * 如果数组大小比集合元素多,那么在数组满足集合元素后在末尾设置为null
  35. *
  36. * 如果在这个集合中指定数组运行时类型不是运行时元素类型的超类,那么抛ArrayStoreException异常
  37. * 如果指定数组为空,则抛出NullPointerException
  38. */
  39. <T> T[] toArray(T[] a);
  40. //修改操作
  41. /**
  42. * 确保此集合包含特定的元素类型。
  43. * 如果此集合增加元素成功返回true。
  44. * 如果此集合不允许有重复元素并且已经包含所传参数,那么返回false
  45. *
  46. * 支持此操作的集合可能会限制向该集合添加哪些元素。特别的,有些集合会拒绝null元素,有些
  47. * 会对要增加的元素强加一些限制。
  48. * Collection实现类应该在文档中明确指出所有的限制。
  49. *
  50. * 如果集合以除已经包含元素之外的任何原因拒绝添加特定元素,则必须抛出异常
  51. *(而不是返回false)。这保留了集合在此调用返回后始终包含指定元素的不变式。
  52. */
  53. boolean add(E e);
  54. /**
  55. * 如果此集合中存在此元素,那么移除一个特定元素类型的实例。更正式的说,如果集合中包含一个或多个这样的元素,
  56. * 那么删除这样的元素(o==null?e==null:o.equals(e))。如果集合包含指定的元素(或集合因调用而发生改变),那么返回true。
  57. *
  58. * 如果指定元素的类型和集合不相容,抛出ClassCastException异常(可选的限制条件)
  59. * 如果指定元素是null并且这个集合不允许null元素存在,那么抛出NullPointerException异常(可选的限制条件)
  60. * 如果此集合不支持remove操作那么抛出UnsupportedOperationException异常(可选的限制条件)
  61. */
  62. boolean remove(Object o);
  63. //容量操作
  64. /**
  65. * 如果this集合包含指定集合的所有元素,返回true
  66. * c集合必须要检查是否被包含在this集合
  67. * 如果指定元素的类型和集合不相容,抛出ClassCastException异常(可选的限制条件)
  68. * 如果指定元素是null并且这个集合不允许null元素存在,那么抛出NullPointerException异常(可选的限制条件)
  69. */
  70. boolean containsAll(Collection<?> c);
  71. /**
  72. * 将指定集合的所有元素到this集合中(可选的操作)。
  73. * 如果指定的集合在操作进行时被修改了,那么此操作行为未定义。
  74. * (这意味着如果指定的集合是这个集合,并且这个集合是非空的,那么这个调用的行为是未定义的。)
  75. *
  76. * @参数:c集合包含了要被添加到这个集合的元素
  77. * @返回:如果调用结果改变了this集合返回true
  78. * @throws:如果 addAll操作不被此集合支持,那么抛出UnsupportedOpertaionException异常
  79. * @throws: 如果指定集合包含了空元素而this集合不允许有空元素,那么抛出NullPointerException异常
  80. * @throws:如果this集合阻止hiding集合元素类型添加,那么抛出ClassCastException异常
  81. * @throws:如果指定集合的元素的某些属性阻止将其添加到此集合,则抛出IllegalArgumentException
  82. * @throws:由于插入限制,如果不是所有元素都可以在此时添加,则抛出IllegalStateException异常
  83. */
  84. boolean addAll(Collection<? extends E> c);
  85. /**
  86. * 移除此集合中所有的包含在指定集合中的元素(可选的操作)。调用过此函数之后,那么此集合和指定集合将不再包含相同元素。
  87. *
  88. * @param:包含要从该集合中删除的元素的c集合
  89. * @return:如果此集合因调用而更改那么返回true
  90. * @throws:如果此集合不支持removeAll方法,则抛出UnsupportedOperationException
  91. * @throws:如果集合中一个或多个元素的类型与指定集合不兼容,则抛出ClassCastException(可选的操作)
  92. * @throws:如果该集合包含一个或多个空元素,且指定的集合不支持空元素(optional),或者指定的集合为空,
  93. * 则抛出NullPointerException异常
  94. */
  95. boolean removeAll(Collection<?> c);
  96. /**
  97. * 移除此集合中所有符合给定Predicate的元素。在迭代期间或由Predicate引发的错误或运行时异常将被转发给调用方
  98. * @implSpec
  99. * 默认实现使用其迭代器遍历集合的所有元素。每一个匹配的元素使用iterator.remove()来移除。
  100. * 如果集合的iterator不支持移除将会抛出UnsupportedOperationException异常在匹匹厄到
  101. * 第一个元素时。
  102. * @param 过滤一个predicate
  103. * @param 筛选要删除的元素返回true的Predicate
  104. * @return 如果任何一个元素被删除返回true
  105. * @throws 指定过滤器为空,抛出NullPointerException
  106. * @throws 如果元素没有被删除,或者移除操作不支持,
  107. * 则立即抛出UnsupportedOperationException异常
  108. * @since 1.8
  109. */
  110. default boolean removeIf(Predicate<? super E> filter) {
  111. //如果filter为null抛出NullPointerException
  112. Objects.requireNonNull(filter);
  113. boolean removed = false;
  114. final Iterator<E> each = iterator();
  115. //通过迭代器遍历删除,只要删除一个removed就为true
  116. while (each.hasNext()) {
  117. if (filter.test(each.next())) {
  118. each.remove();
  119. removed = true;
  120. }
  121. }
  122. return removed;
  123. }
  124. /**
  125. * 只保留此集合中包含在指定集合中的元素(可选的操作)。
  126. * 也就是说,此集合中不包含在指定集合中的所有元素。
  127. * @param:要保留的元素的集合c
  128. * @return:如果此集合改变了返回true
  129. * @throws:如果此集合不支持retainAll,则抛出UnsupportedOperationException异常
  130. * @throws:如果集合中一个或多个元素的类型与指定集合不兼容,则抛出ClassCastException(可选的操作)
  131. * @throws:如果该集合包含一个或多个空元素,且指定的集合不支持空元素(optional),或者指定的集合为空
  132. */
  133. boolean retainAll(Collection<?> c);
  134. /**
  135. * 移除此集合中所有元素(可选操作),调用此方法后集合里将为空。
  136. * @throws: 如果此集合clear操作不支持将会抛出UnsupportOperationException异常。
  137. */
  138. void clear();
  139. //比较和hash
  140. /**
  141. * 比较指定的对象与此集合是否相等
  142. *
  143. */
  144. boolean equals(Object o);
  145. /**
  146. *返回这个集合的hashCode。当集合接口没有对Object.hashCode方法的一般协议做任何规定,编程
  147. *人员应该注意在重写equals方法时必须重写hashCode方法,以便满足一般协议对这个
  148. *Object.hashCode方法。特别的,c1.equals(c2)表明c1.hashCode()==c2.hashCode()。
  149. */
  150. int hashCode();
  151. /**
  152. *一下1.8新增的Spliterator,暂不研究
  153. */
  154. @Override
  155. default Spliterator<E> spliterator() {
  156. return Spliterators.spliterator(this, 0);
  157. }
  158. default Stream<E> stream() {
  159. return StreamSupport.stream(spliterator(), false);
  160. }
  161. default Stream<E> parallelStream() {
  162. return StreamSupport.stream(spliterator(), true);
  163. }
  164. }

AbstractCollection是一个实现了Collection接口的抽象类,Collection接口中声明的方法,比如contains、isEmpty等方法在AbstractCollection抽象类中均已实现,其他集合类继承该抽象类复用里面已实现的方法即可。

Collection接口具体的派生接口和实现类如下图:
Collection接口概述 - 图1
下面分别介绍List接口分支和Set接口分支。

2、List接口

Collection接口有两大派生接口:List和Set。List接口实现类的集合,元素有序且可以重复,集合中的每个元素都可以通过索引(index)一一对应。List接口继承了Collection接口,并新添加了一些根据索引操作集合里元素方法:

// 在index位置插入元素,后面的元素都往后移一个元素
add(int index, Object ele);

// 在index位置插入集合
boolean addAll(int index, Collection eles);

// 返回index位置的元素
Object get(int index);

// 返回list集合中第一次出现o对象的索引位置,如果list集合中没有o对象,那么就返回-1
int indexOf(Object obj);

// 返回list集合中最后一次出现o对象的索引位置,如果list集合中没有o对象,那么就返回-1
int lastIndexOf(Object obj);

// 删除index位置的元素
Object remove(int index);

// 设置index位置的元素
Object set(int index, Object ele);

// 获取集合的子集
List subList(int fromIndex, int toIndex);

// 获取list迭代器
ListIterator<E> listIterator();

// 初始化一个指向list中索引为index的迭代器
ListIterator<E> listIterator(int index);

上面新增的方法和接口,重点介绍一下List接口特有的listIterator(),它与Iterator接口最大的不同之处在于,iterator只能正向遍历元素,listIterator()可以返回前面的元素,且listIterator支持边遍历,边修改(set和add)list,listIterator接口有两种形式:

ListIterator<E> listIterator();

// 初始化一个指向list中索引为index的迭代器
ListIterator<E> listIterator(int index);

listIterator接口的API如下:

// 正序迭代查看是否有下一个元素有返回true没有false 
boolean hasNext();

// 返回当前光标位置的下一个元素(注意,当迭代器位置处于列表最后一个元素的右面的时候,调用next()方法会出现异常,因为光标后面已经没有元素了)
E next();

// 反序迭代
boolean hasPrevious();

// 反序返回当前光标位置的上一个元素
E previous();

// 正序获取最后一次执行next()方法返回的元素的下标
int nextIndex();

// 反序获取最后一次执行previous()方法返回的元素的下标
int previousIndex();

// 从列表中删除最后一次执行next()或previous()方法返回的元素。
// (注意:1.调用remove()方法之前,一定要有next()或previous()方法执行,否则报错:java.lang.IllegalStateException
// 2.执行next() 或 previous()之后与执行remove()之前,不能执行add(E)方法,否则报错:java.lang.IllegalStateException)
void remove();

// 用参数元素代替next() 或 previous()返回的最后一个元素
// (注意:在调用set(E)方法前,不能调用remove() 和 add(E) 方法,否则会报错:java.lang.IllegalStateException
//因为调用remove()或add(E)方法之后,指针并没有指向哪个元素,只是处于了一个位置)
void set(E e);

//将指定元素插入到previous()返回的元素与当前光标位置之间(注意:此时调用next()方法,仍然返回的是下一个元素,调用previous()返回的就是新插入的元素了。 值得注意的是,当光标位置处于0(第一个元素左边)时,调用add(E)方法会出现异常,因为现在光标左边还没有元素)
void add(E e);

ListIterator和Iterator的区别:

  1. ListIterator初始化时可以指向list集合中的特定index元素,而Iterator不支持;
  2. Iterator仅能正向遍历,ListIterator既能正向遍历,又能反向遍历;
  3. ListIterator相比Iterator,多了add、set和remove方法,支持在遍历list集合的同时,对集合进行修改;
  4. ListIterator可以定位当前的索引位置,通过nextIndex()和previousIndex()实现,Iterator没有此功能;

List接口的实现类,常用的有两个: ArrayList和 LinkedList,以及不常用的Vector,下面分别介绍。

2.1 ArrayList实现类

请参考我这篇博客:https://www.yuque.com/docs/share/1853b631-5f53-477e-9004-fd8a81bd0014?# 《ArrayList》

2.2 LinkedList实现类

请参考我这篇博客:https://www.yuque.com/docs/share/b5938b66-7155-4fbd-9750-14d795cb1f90?# 《LinkedList》

2.3 Vector实现类

Vector目前基本不会使用,如果一定要使用线程安全的Collection接口的实现类,还是推荐使用CopyOnWriteArrayList类。
有关Vector实现类:

  • Vector的底层实现是动态数组;
  • Vector是线程安全的,涉及到多线程的方法都加了synchronized锁;
  • Vector的默认容量大小是10,当Vector容量不足以容纳全部元素时,Vector的容量会增加。若容量增加系数 >0,则将容量的值增加“容量增加系数”;否则,将容量大小增加一倍。

    3、Set接口

    实现Set接口的集合类是一个不包含重复元素的Collection,最多包含一个null元素。实现Set接口的类主要有HashSet和TreeSet两个,下面分别介绍这两个类。

    3.1 HashSet实现类

    HashSet底层的数据结构是一个HashMap,如下:
    private transient HashMap<E,Object> map;
    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();
    
    HashSet的构造函数中也是new了一个这样的HashMap,以HashSet的无参构造器为例:
    public HashSet() {
          map = new HashMap<>();
      }
    
    HashSet里的元素,其实就是这个底层的HashMap的key的集合,val是上面的PRESENT ,HashSet里的方法,比如add、contains和clear方法,底层都是对这个HashMap进行操作的,如下:
    public boolean contains(Object o) {
          return map.containsKey(o);
      }
    public boolean add(E e) {
          return map.put(e, PRESENT)==null;
      }
    public void clear() {
          map.clear();
      }
    
    需要注意的是,HashSet判断两个对象是否相同的方法时继承自Object类的equals方法,这点与TreeSet不同。

    3.2 TreeSet实现类

    相比HashSet中的元素是无序的,TreeSet可以实现元素的排序,分为自然排序和定制化排序两种。自然排序比如TreeSet里存入Integer对象,会自动从小往大排序。但如果我们自定义一个Entity类,向TreeSet集合里存放Entity类的对象时,Entity类必须实现Comparable接口中的compareTo()方法,该方法不仅可以比较是否相等,还可以比较大小,如果相等返回0,调用者大于参数则返回正数,否则返回负数。
    实现了Comparable接口的类的自然序是按类的对象从小到大的顺序排的 ,至于你定义对象怎么比较大小,就是在compareTo()方法里实现的,compareTo方法返回正数,代表this对象大于入参对象,排自然序时会把这个this对象排在入参对象后面。
    关于自定义排序,可以参考我这篇博客:https://www.yuque.com/docs/share/aa5c1e15-f929-4010-9dfb-e389715e428e?# 《自定义排序》

举例说明一下:
(1)自然排序

public class Demo {
    public static void main(String[] args) {
        Set set = new TreeSet<Integer>();
        set.add(2);
        set.add(8);
        set.add(0);
        set.add(-1);
        System.out.println(set);
    }
}

执行结果如下:

[-1, 0, 2, 8]

可见由于Integer对象有自然顺序特性,存入TreeSet后会按从小打到的顺序排序。

(2)自定义排序
以上面的Student类为例,将Student对象存入TreeSet集合中,main方法如下:

public class Demo {
    public static void main(String[] args) {
        Set set = new TreeSet<Student>();
        Student student1 = new Student("Jerry", 1);
        Student student2 = new Student("Cissie", 3);
        Student student3 = new Student("Bill", 2);
        set.add(student1);
        set.add(student2);
        set.add(student3);
        System.out.println(set);
    }
}

执行结果如下:

Exception in thread "main" java.lang.ClassCastException: class Basic.EuqalAndHashcode.Student cannot be cast to class java.lang.Comparable (Basic.EuqalAndHashcode.Student is in unnamed module of loader 'app'; java.lang.Comparable is in module java.base of loader 'bootstrap')
    at java.base/java.util.TreeMap.compare(TreeMap.java:1291)
    at java.base/java.util.TreeMap.put(TreeMap.java:536)
    at java.base/java.util.TreeSet.add(TreeSet.java:255)
    at Collection.Demo.main(Demo.java:23)

抛了异常,提示Student类需要实现Comparable接口,改造后的Student类如下:

@EqualsAndHashCode
@ToString
public class Student implements Comparable<Student> {
    private String name;
    private int id;
    public Student(String name, int id)
    {
        this.name = name;
        this.id = id;
    }
    public int compareTo(Student s) {
        return this.id - s.id;
    }
}

改造后的Student类的比较规则是:Student对象的id值越小,则Student对象对应的排序越靠前(从小到大),执行main方法的结果如下:

[Student(name=Jerry, id=1), Student(name=Bill, id=2), Student(name=Cissie, id=3)]

注意是因为在Student类中引入lombok.ToString才允许我们直接打印Student对象,否则需要覆写Object类里的toString()方法。结果是按照Student对象中id属性从小到大排序的,如果想实现id属性从大到小排序,只需改变Student类里的compareTo方法:

public int compareTo(Student s) {
        return s.id - this.id;
    }

参考

Java集合(二)、Collection接口介绍 - 黎先生 - 博客园 www.cnblogs.com
Java集合(三)、继承自Collection接口的List接口 - 黎先生 - 博客园 www.cnblogs.com
Java集合(四)、继承自Collection接口的Set接口 - 黎先生 - 博客园 www.cnblogs.com
Java 集合中关于Iterator 和ListIterator的详解 blog.csdn.net
https://www.cnblogs.com/kuoAT/p/6771653.html www.cnblogs.com
Iterator和ListIterator区别以及详细的API解析 - 李玉杰 - 博客园 www.cnblogs.com