这篇文章记录了Collection集合,List集合,Set集合
在文章第七点总结了两大系列集合的五种实现类的区别,有需要的小伙伴可以直接去查看
一、什么是集合
集合是Java中存储对象数据的一种容器
二、集合有什么特点
- 大小不固定,类型也可以不固定(通常需要泛型约束)
- 集合只能存储引用数据类型
- 集合适合对容器中的元素进行增删操作
三、体系结构

Collection单列集合,每个元素(数据)只包含一个值。
Map双列集合,每个元素包含两个值(键值对)。
四、Collection
1. 什么是Collection
官方的解释:
- Collection是集合层次结构中的根接口。 集合表示一组对象,称为其元素 。 有些集合允许重复元素而其他集合则不允许。 有些是有序的,有些是无序的。
- JDK不提供此接口(Collection)的任何直接实现:它提供了更具体的子接口的实现,如 Set 和 List
- 此接口通常用于传递集合并在需要最大通用性的情况下对其进行操作。
说的通俗一点就是:
- Collection是根接口,所有集合都来自Collection
- jdk不提供该接口实现类对象,但提供了更具体的实现类
- 就好比父类和子类,你可以用父类对象接收一个子类的实例化对象
例如:Father f = new Sun(); // Sun类继承了Father类
2. Collection的体系结构

3. 常用方法
方法名称
| 说明
| public boolean add(E e)
| 把给定的对象添加到当前集合中
| public void clear()
| 清空集合中所有的元素
| public boolean remove(Object o)
| 把给定的对象在当前集合中出现的第一个位置删除,如果删除失败返回false
| public boolean contains(Object obj)
| 判断当前集合中是否包含给定的对象
| public boolean isEmpty()
| 判断当前集合是否为空
| public int size()
| 返回集合中元素的个数。
| public Object[] toArray()
| 把集合中的元素,存储到数组中
| 4. 集合的遍历方式
4.1 迭代器
Iterator是集合专用的迭代方法,只能集合调用
Iterator中常用方法
- next():返回迭代中的下一个元素
- hasNext():如果迭代具有更多元素,则返回true
- Collection<Integer> list = new ArrayList<>();
- Iterator<Integer> iterator = list.iterator();
- while(iterator.hasNext()){
- Integer next = iterator.next();
- System.out.println(next);
- }
复制代码 注意事项:
- 迭代器只能使用一次
- next()移动指针到下一个元素,如果没有抛异常
- hasNext()检查下一个元素是否为空,但不移动指针
- 迭代器迭代元素越界出现:NoSuchElementException
4.2 普通for
只适用于List系列集合,因为他有序
通过调用集合的get方法,根据索引取值
4.3 增强for
- List<Integer> list = new ArrayList<>();
- list.add(1);
- list.add(3);
- list.add(2);
- //增强for
- for (int i : list) {
- if (i == 3) {
- i = 5;//增强for中如果对取出的这个元素修改,将不会对集合产生影响
- }
- System.out.println(i);
- }
复制代码 4.4 Lambda迭代
- // 使用lambda表达式集合的迭代
- colStr.forEach(new Consumer() {
- @Override public void accept(String s) {
- System.out.println(s);
- }
- });
- // 简化写法
- System.out.println("-------------------------");
- colStr.forEach(s-> System.out.println(s));
- System.out.println("-------------------------");
- // 再次简化
- colStr.forEach(System.out::println);
复制代码 五、List
Collection的子接口
1. 特点
有序,有索引(是独有的),可重复,可存储null值,此实现不同步,线程不安全
存储和取出顺序一致
2. 特有方法
方法名称
| 说明
| 注意
| void add(int index, E element)
| 将给定的元素插入到指定位置
| 索引不能越界,否则报错
| E get(int index)
| 返回该索引位置的元素,没找到返回-1
| E set(int index, E element)
| 对给定位置的元素进行替换
| E remove(int index)
| 删除指定位置的元素
| int lastIndexOf(Object o)
| 返回特定元素在集合中最后一次出现的位置
| 没找到返回-1
| int indexOf(Object o)
| 返回特定元素在集合中第一次出现的位置
| ListIterator listIterator()
| List集合特有的迭代器
|
| List subList(int fromIndex, int toIndex)
| 根据开始索引和结束索引(左闭右开)返回一个新的集合,该集合是原集合的子集
|
| ListIterator叫做列表迭代器,将在第3.2解释
3. 迭代方式
和Collection一样的:迭代器、增强for、Lambda表达式
List独有的:listIterator、普通for(因为有索引)
3.1 并发修改异常
迭代器在迭代集合,但是集合本身被修改,换而言之:就是在同一时刻只能有一个对象来操作集合,否则就会出现并发修改异常
下面两种情况会出现并发修改异常
1. 迭代器和集合方法都会对集合进行操作- List<String> list = new ArrayList<>();
- list.add("java");
- list.add("css");
- list.add("python");
- while(stringListIterator.hasNext()){
- String ele = stringListIterator.next();
- if("css".equals(ele)){
- stringListIterator.remove();//允许
- //list.remove("css");// 不允许 出现 ConcurrentModificationException
- }
- System.out.println(ele);
- }
复制代码 2. 增强for本身也是一个Iterator迭代器,同样不能使用- List<String> list = new ArrayList<>();
- list.add("java");
- list.add("css");
- list.add("python");
- for (String s : list) {
- if("css".equals(s)){
- list.remove("css");//不允许,并发修改异常
- }
- System.out.println(s);
- }
复制代码 不会出现并发修改异常
- List<String> list = new ArrayList<>();
- list.add("java");
- list.add("css");
- list.add("python");
- for (int i= 0; i < list.size();i++){
- String ele = list.get(i);
- if("css".equals(ele)){
- list.remove("css");// 允许
- }
- }
- System.out.println(list);
复制代码 并发修改异常总结
- 同一时间只能有一个对象操作集合
- 使用迭代器要保证集合不在改变
- 出现异常的原因:
- 迭代器在进行遍历过程中,如果对数组进行操作,会导致迭代器的实际修改值和集合的预期修改值不对应,就会出异常
- 迭代器是一次性的,如果对集合进行操作,没有及时更新迭代器就会引发异常
- 如何解决并发修改异常
- 使用普通for:用索引取值,同一时间只有一个对象对集合操作
- 使用列表迭代器ListIterator
3.2 ListIterator
boolean hasNext()
| 从前往后遍历
| boolean hasPrevious()
| 从后往前遍历,前提是先得从前往后遍历一遍,让迭代器指针走到末尾
|
- List<String> list = new ArrayList<>();
- ListIterator<String> listIterator = list.listIterator();
复制代码
- ListIterator和Iterator的区别
- ListIterator每进行一次迭代都会把实际修改值重新赋值给预期修改值,但是Iterator不行
- ListIterator是List集合特有的,Iterator是所有集合都有的
4. 两个主要的接口实现类
4.1 特点
List的接口实现类,List的特点就是ArrayList、LinkedList的特点
4.2 ArrayList
- 使用的是数组,默认容量为10
- 一旦数组满了,则创建一个新的数组,新数组的容量是原数组的容量的1.5倍。并且会将原数组内容复制到新数组
- 因为底层是数组,具有数组的特点:查询快,增删慢

4.3 LinkedList
- 底层是双向链表
- 具有链表的特点:查询慢,增删快
- 此实现不同步,线程不安全
- 常用方法



六、Set
1. 特点
元素唯一,无序,没有索引,最多只能一个null
三个主要的接口实现类
HashSet、TreeSet、LinkedHashSet
2. HashSet
2.1 特点:无序(HashSet只是不保证有序,并不是保证无序),不重复,无索引,此实现不同步
2.2 底层实现:哈希表,是一种对增删改查性能都较好的结构
jdk8之前:数组+链表
jdk8之后:数组+链表+红黑树
- 哈希值
- 哈希值是jdk根据对象计算得出的内存地址,根据某个规则算出来的int类型数值
- 对象可以通过调用hashCode()返回对象的哈希值
- 同一个对象的哈希值是相同的
- 在默认情况下,不同对象的哈希值是相同的(除非重写了hashCode方法)
2.3 HashSet底层存储结构

- 当调用无参hashSet构造器,会默认创建一个长度为16的数组
- 添加元素,元素哈希值和数组长度根据哈希算法计算元素存储位置
- 判断该位置是否为空,如果为空直接存数组
- 如果位置不为空,判断哈希值,如果不同直接存链表
- 如果哈希值相同,判断equals是否相同,如果不同直接存链表
- 如果都相同就不存
jdk8之前:新元素占据旧元素位置,指向旧元素。当元素链表挂载元素过多会造成查询性能下降。
jdk8之后:新元素挂在旧元素下面。当链表长度超过8时,自动将链表转换为红黑树,进一步提高性能
- 判断hashcode值是否相同,如果不同,则不重复
- 如果hashcode相同,调用equals比较,如果为true,则重复。否则挂在元素下边
- 在类中可以重写equals和hashCode方法,进行自定义特征比较
- //假设这是一个学生类,有name和age两个属性
- //重写equals和hashCode方法
- @Override
- public boolean equals(Object o) {
- if (this == o)
- return true;
- if (o == null || getClass() != o.getClass())
- return false;
- Student student = (Student) o;
- return Objects.equals(name, student.name); //例如这里
- //可以自定义比较对象中的哪一个特征,比如就比较名字是否一样,不在乎年龄是否一样
- }
- @Override
- public int hashCode() {
- return Objects.hash(name); //例如这里
- //可以自定义要进行计算哈希的对象,比如只想判断名字哈希是否一样,
- //名字哈希一样就认为这俩对象哈希一样,不在乎年龄哈希是否一样
- }
复制代码 3. TreeSet
3.1 特点:默认自然升序排序,不重复,无索引,可排序
3.2 排序方式
- 必须实现Comparable接口,重写compareTo,定义排序规则
- Integer、String这些类默认已经实现了Comparable接口,可以直接去调用
- 英文按照字母顺序,中文按照Unicode码大小升序
创建集合时候就实现Comparator接口
如何理解排序呢
首先要知道这俩的区别
- Comparable是比较接口,Comparator是比较器
- 实现比较接口(Comparable)就意味着该类支持排序
- 实现比较器(Comparator)就意味着该集合要使用自定义的比较方法
- Comparable相当于“内部比较器”,而Comparator相当于“外部比较器”。
通俗一点,用人话说就是:
- 使用Comparable就是支持排序,意思是我这个类支持排序
- 使用了Comparator就是比较器,不管类支不支持,想要排序就得按照我比较器的规矩排序
LinkedHashSet
- 特点:有序(存入和取出顺序一样),不重复,无索引
- 底层存储结构:哈希表和双向链表,双向链表用来记录存储的顺序
七、所有集合最全面总结
| List | Set | 实现类 | ArrayList | LinkedList | HashSet | TreeSet | LinkedHashSet | 顺序 | 有序 | 有序 | 无序 | 有序 | 有序 | 重复 | 可重复 | 可重复 | 不可重复 | 不可重复 | 不可重复 | 空值 | 允许多个null | 允许多个null | 最多一个null | 最多一个null | 最多一个null | 索引 | 有索引 | 有索引 | 无索引 | 无索引 | 无索引 | 排序 | 存入顺序就是取出顺序 | 存入顺序就是取出顺序 | 不能排序 | 自然排序和比较器排序 | 存入顺序就是取出顺序 | 特点 | 查询快 | 增删首尾操作快 | 增删改查都快的五边形战士 | 唯一可以自定义排序 | 增删改查都快的五边形战士 | 底层 | 数组 | 链表 | 哈希表 | 红黑树 | 哈希表和双链表 | 凡是带list都可重复、有索引 | 凡是带set都不可重复、无索引 | 凡是带hash都是五边形战士,增删改查都快 | 只有HashSet无序、不能排序 | 八、集合工具类Collections
Collections 不属于集合,是用来操作集合的工具类

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |