前言

  1. 关于对集合容器的大部分理解,后续慢慢增加,从为什么学习集合,集合有什么作用,到集合各种api的学习与理解。最后集合的相关面试题。

一、学习目标

一、Java集合学习指南

1.1 为什么要学习Java集合

  • 方便对多个对象进行操作,将对象存储在容器中

image.png

1.2 如何学习Java集合

  • 首先应该学习Java的基本用法,以及从它的整体去了解什么是集合
  • 集合为了方便我们去操作多个对象给我们提供了一系列的API,我们要去学习如何使用这个方法
  • 我么学习了这些基本的API去操作对象之后,应当从面向对象的角度去理解为什么要抽象出多个接口,以及这些接口的特性
  • 我们可以总结出几个常用的实现类,并且了解这些实现类的数据结构,什么时候使用这些类
  • 需要学习和了解的数据结构

image.png
image.png

1.3 学习进阶与面试

  • Java集合是面试的重点 ,从基础用法到源码一步步深入。
  • 下面取一些可能会问到的问题

    • HashMap的数据结构是什么?他是怎么扩容的?底层有没有用红黑树?取Key Hash值是JDK源码是怎么实现的?为什么要这样做?简单作答一下
    • HashMap是线程安全的吗?什么是线程安全?有什么更好的解决方案?那线程安全的HashMap是怎么实现的?简单作答一下
    • HashSet是如何判断Key是重复的?简单作答一下你是《未来世界的幸存者》么?你是小贱

      二、集合类

      一、集合框架的概述

      面向对象对事物的体现都是以对象的形式。为了方便对多个对象进行操作,那我们就需要将对象存储起来,Array数组储存对象有他的弊端,而java集合像一个容器,可以动态的将多个对象的引用放入容器中。
      a) 数组在内存存储方面的特点:
      i. 数组在初始化以后,长度就确定了
      ii. 数据声明的类型,就决定了进行元素初始化时的类型
      b) 数组存储对象的弊端:
      i. 数组初始化以后,长度就不变了,不利于拓展
      ii. 数组中提供的属性和方法少,不利于进行添加 ,删除,插入等操作,并且效率不高。同时无法直接获取存储元素的个数。
      iii. 数组存储的数据是有序的,可以重复的 —à 存储数据的特点单一
      Java集合可以用于存储数量不等的多个对象,还可以用于保存具有映射关系的关联数组。
      image.png

      二、集合的体系

      2.1 Collection接口和Map接口

  • Collection接口:单列数据,定义了一组存储对象的方法的集合

    • List:元素有序,可重复的集合
    • Set:元素无序,不可重复的集合
  • Map接口:双列数据,保存具有”KEY-VALUE”映射关系的集合

三、Collection接口

一、Collection介绍

1.1 Collection的结构

Collection接口是List,Map和Queue接口的父接口。该接口里面的方法均可用于操作List接口,Map接口,Queue接口。
JDK不提供该接口的任何直接实现,而是提供了更具体的子接口实现。
在Java5之前Java集合会丢失容器中所有对象的数据类型,将对象当成Object类型处理。从5.0之后,增加了泛型,Java集合可以记住容器中对象的数据类型。

1.2 Collection的由来

  • 集合可以存储多个元素,但我们对多个元素也有不同的需求
    • 多个元素,不能有相同的
    • 多个元素,能够按照某个规则排序
  • 针对不同的需求:java就提供了很多集合类,多个集合类的数据结构不同。但是,结构不重要,重要的是能够存储东西,能够判断,获取
  • 把集合共性的内容不断往上提取,最终形成集合的继承体系——>Collection

    1.3 Collection的基础功能

    image.png
  1. 添加 add (Objecet obj )add(collertion c)

Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new Person(“Jerry”,20));
coll.add(new String(“Tom”));
coll.add(false);

  1. 获取元素的有效个数 int size()
  2. 清空集合 void clear()
  3. 是否包含空集合 Boolean isEmpty()
  4. 是否包含某元素 a) boolean contains(Object obj) 通过元素的equals()来判断是否相等b) boolean containsAll(Collection c) 拿两个集合的元素挨个比较,也是通过equals 来比较的。
  5. 删除 a) boolean remove(Object obj)通过equals()找到第一个元素 删除第一个元素b) boolean removeAll(Collection c) 取两个集合的差集
  6. 取两个集合的交集 boolean retainAll(Collection c)
  7. 集合是否相等 boolean equals(Collection c)
  8. 转成对象数组 Object[] toArray()
  9. 获取对象的哈希值 hashCode()
  10. 遍历 iterator() 返回迭代器对象 用于集合对象

    二、迭代器(Iterator)介绍

    2.1 使用Iterator接口遍历集合元素

  11. Iterator对象称为迭代器(设计模式的一种),主要用于遍历Collection集合中的元素。

  12. 提供一种方法访问容器对象中的各个元素,而又不需要暴露该对象的内部细节。迭代器模式就是为容器而生。
  13. Collection接口继承了Java.lang.iterable接口,该接口有一个Iterator()方法,所有继承了Collection接口的集合类都有一个Iterator()方法,用以返回一个实现了Iterator()方法的对象。
  14. Iterator仅用于遍历集合。Iterator本身并不具有承载对象的能力,如果需要创建Iterator对象,则必须有一个被迭代的对象。
  15. 集合对象每次调用Iterator()都会创建一个全新的迭代器对象。默认游标都在第一个元素之前。

image.png
在调用it.next()方法之前都要调用it.hasNext()进行检测。否则下一条记录无效。若直接调用next()方法,抛出NoSushElementException()异常。
image.png

2.2 迭代器的删除元素

  1. 迭代器可以删除集合中的元素 但是不是调用Collection的remove() 。是迭代器对象自己的remove()方法。
  2. 如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法, 再调用remove都会报IllegalStateException。

Iterator iterator = coll.iterator();
while (iterator.hasNext()){

  1. Object next = iterator.next();<br /> if ("Tom".equals(next)){<br /> iterator.remove();<br /> }<br />}

2.3 foreach循环遍历集合

  1. Java5.0提供了foreach循环迭代访问集合和数组。
  2. 遍历操作不需要获取Collection或者数组的长度,无需使用索引访问元素。
  3. 遍历集合的底层调用Iterator完成操作。
  4. foreach还可以用来遍历数组。

image.png

  1. 判断foreach遍历数组的输出结果?

String[] str = new String[5];
for (String myStr : str) {
myStr = “atguigu”;
System.out.println(myStr);
}
for (int i = 0; i < str.length; i++) {
System.out.println(str[i]);
}
输出结果为atguigu atguigu atguigu atguigu atguigu null null null null null
因为foreach知识遍历数组,并不改变数组。
若是想要改变数组,则有下面的案例。使用普通for循环遍历并进行数组赋值操作。
for (int i = 0; i < str.length; i++) {
str[i] = i+”1”;
}
for (int i = 0; i < str.length; i++) {
System.out.println(str[i]);
}

三、List接口介绍

3.1 List接口概述

  1. 鉴于Java中数组存储数据的局限性,通常我们使用List来代替数组。
  2. List集合类中元素有序,可重复。集合中的每个元素都有其对应的顺序索引。
  3. List容器中的元素都有其对应的整数型的序号,我们可以通过序号存取容器中的元素。
  4. List接口常用实现类I. ArrayList 底层数据结构是数组。线程不安全。II. LinkedList 底层数据结构是链表。线程不安全。III. Vector 底层数据结构是数组。线程安全(过时)。

    3.2 List接口方法

  • 继承父接口的方法之外,List添加了根据索引操作集合中的元素。
  1. void add(int Index,Object ele) 在index位置中添加ele元素
  2. boolean addAll(int index,Collection eles) 在index位置中将所有的元素eles全部加入进来
  3. Object get(int index) 获取指定index位置元素
  4. int indexOf(Object obj) 返回obj在集合中首次出现的位置
  5. int lastIndexOf(Object obj) 返回obj 在当前集合中末次出现的位置
  6. Object remove(int index) 移除index位置上的元素并返回此元素
  7. Object set(int index,Object ele) 指定index位置上的元素为ele
  8. List subList(int fromIndex,int toIndex) 返回从fromIndex到toIndex的子集合
  • 如图所示为List接口所有的接口方法image.png

    四、Set接口介绍

    4.1 Set接口概述

  • set接口是Collection接口的子接口,set接口没有提供额外的方法。

  • Set集合不允许包含相同的元素,如果添加相同的元素,则会添加失败。元素不可重复
  • Set判断两个对象是否相同不是使用==比较,而是使用equals()比较。

    4.2 Set常用子类

  • HashSet集合

    • 底层数据结构是哈希表(一个元素为链表的数组)
  • TreeSet集合
    • 底层数组结构是红黑树
    • 保证元素的排序方式
  • LinkedHashSet集合
    • 底层数据结构是哈希表+链表

      四、List集合

      一、ArrayList分析

      1.1 ArrayList介绍

  1. ArrayList是List接口主要实现类
  2. ArrayList线程不安全,查找效率高,并且可以存放null
  3. 底层是一个数组,可以实现动态增长,因为他又扩容这么一个机制。

    1.2 ArrayList底层源码分析

  4. jdk8中,ArrayList底层创建了一个Object[] elementData 的数组,默认大小为0的空数组。当我们使用它的时候,会自动创建一个大小为10的数组。每次add操作的时候,都会先检查是否需要扩容。默认情况下回扩容为原来的1.5倍,并将原有数组中的数据复制到新的数组中。扩容的方法grow() ,复制的方法copyOf()

  5. 而在jdk7中,底层是初始化一个大小为10的数组。后续再进行扩容。
  6. 实际上jdk7类似于单例中的饿汉式。jdk8类似于单例中的懒汉式,延迟了数组的创建,节省内存。
  7. 因为Arraylist拥有扩容这个概念,因此ArrayList是基于动态数组实现的,在增删时候需要数组的拷贝复制。
  8. 删除的时候并不会减少容量。
    a) 结构示意图
    image.png
    b) 源码示意图介绍
    1. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/22720063/1637247868208-e3cbbc46-fe15-4749-85a1-5c034c2d5591.png#clientId=u38ba0a11-0fda-4&from=paste&id=u6efc5634&margin=%5Bobject%20Object%5D&name=image.png&originHeight=392&originWidth=463&originalType=url&ratio=1&size=101681&status=done&style=none&taskId=u08dc066d-5064-469a-b989-b1ed588956d)

    二、 ArrayList和Vector的区别

  • vector是jdk1.2中比较古老的集合实现类
  • vector和Arraylist底层都是数组,区别就是同步(线程安全)
  • 一般我们不考虑同步的情况下使用ArrayList.考虑同步的情况下使用Collections中的方法来实现同步。Collections.synchronizedList(new ArrayList(…))
  • 这种情况下使用Collections的方法来实现同步浪费资源,有一个实现类copyOnWriteArrayList可以实现同步
  • 还有另外一个区别,ArrayList在原来的基础上扩容0.5被,而Vector扩容一倍。

    问:为什么Java不推荐使用Vector?

  1. 因为vector是线程安全的,所以效率低,这容易理解,类似StringBuffer
  2. Vector空间满了之后,扩容是一倍,而ArrayList仅仅是一半
  3. Vector分配内存的时候需要连续的存储空间,如果数据太多,容易分配内存失败
  4. 只能在尾部进行插入和删除操作,效率低

    三、LinkedList分析

    3.1 LinkedList介绍

  • 底层的数据结构是双向链表,线程不安全
  • 对于频繁的插入或者删除元素的操作。使用LinkedList,效率较高。

image.png

3.2 新增方法(了解)

```
void addFirst(Object obj)

• void addLast(Object obj)

• Object getFirst()

• Object getLast()

• Object removeFirst()

• Object removeLast()
```

3.3 底层源码实现

  1. 数据结构a) 双向链表,内部没有声明数组。而是定义了Node类型的first和last,用于记录首末位置。同时定义内部类Node,作为LinkedList中保存数据的基本结构。b) Node除了保存数据,还定义了两个变量。i. prev变量记录前一个元素的位置。ii. next变量记录下一个元素的位置。

image.png

四、List集合总结

五、Set集合

一、HashSet分析

1.1 HashSet介绍

  1. HashSet按Hash算法来存储集合中的元素,因此具有很好的存储,查找,删除等功能。
  2. HashSet的特点
    1. 不能保证元素的顺序
    2. HashSet不是线程安全的
    3. 可以存储null
  3. HashSet是不允许存储相同的元素,判断两个元素是否相同,是通过两个对象的HashCode()方法来比较相等,并且两个对象的equals()方法返回值相等。
  4. 对于存放Set容器中的对象,对应的类一定要重写equals()方法和hashCode()方法,以实现对象相等规则,“相等的对象必须有相等的散列码
  5. 底层也是数组+链表(单个元素为链表结构)

    1.2 HashSet存储过程

  6. 向HashSet集合中存储一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据hashCode值通过某种散列函数决定该对象再HashSet底层数组中的存储位置。

  7. 如果两个元素的hashCode()值相等,会继续调用equals()方法,如果equals()方法返回值为true,添加失败;如果为false,那么会保存该元素,但是该数组的位置已经有元素了,那么会通过链表的方式继续链接。
  8. 如果两个元素的equals()返回值相等,但他们的hashCode()返回值不相等,hashSet将会把他们存储再不同的位置,但依然可以添加成功。 image.png

    1.3 重写hashCode()方法的基本原则

  9. 在程序运行时,同一个对象多次调用hashCode()方法应该返回相同的值。

    1. 当两个对象的equals()方法比较返回true时,这两个对象的hashCode()方法的返回值应该相同
      1. 对象中用作equals()方法比较的Field,都应该用来计算hashCode()值。

        1.4 HashSet源码分析

        1. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/22720063/1637247869639-64c10780-c8ce-4588-a1f7-bff1322c6e49.png#clientId=u38ba0a11-0fda-4&from=paste&id=u66ee22bc&margin=%5Bobject%20Object%5D&name=image.png&originHeight=355&originWidth=405&originalType=url&ratio=1&size=38633&status=done&style=none&taskId=u4be1f2c5-8b9e-4347-a840-c9043824de8) <br />从源码分析中我们可以看到操作HashSet实际上就是操作Hashmap,因此我们可以在学习到HashMap过后再来深究HashSet

        二、LinkedHashSet分析

        2.1 LinkedHashSet介绍

  • LinkedHashSet是HashSet的子类
  • LinkedHashSet是根据元素的hashCode值来决定元素的存储位置,但他同时使用 双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
  • LinkedHashSet插入性能略低于HashSet,但在迭代访问Set里的全部元素时有很好的性能
  • LinkedHashSet不允许集合元素重复

    2.2 LinkedList源码分析

    a)底层数据结构图
    1. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/22720063/1637247869651-5955053a-bb85-4a29-b910-6202ebbc5bbc.png#clientId=u38ba0a11-0fda-4&from=paste&id=u39af5ec5&margin=%5Bobject%20Object%5D&name=image.png&originHeight=344&originWidth=330&originalType=url&ratio=1&size=25628&status=done&style=none&taskId=uf625325e-ef24-4a8e-a470-60f37ce8e33)

    三、TreeSet分析

    3.1 TreeSet介绍

  • TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态。

  • TreeSet底层使用红黑树结构存储数据

    3.2 红黑树认知

  • TreeSet和后面的TreeMap采用红黑树的存储结构

  • 有序,查询速度比List快

image.png

3.3 TreeSet的两种排序

3.3.1 自然排序(默认)
  • TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素中间的大小,然后将集合元素按升序排列。
  • 如果将一个对象添加到TreeSet时,则该对象必须实现Comparable接口
  • 实现Comparable的类必须实现CompareTo(Object obj)方法,两个对象既通过compareTo(Object obj)方法的返回值来比较大小。
  • Comparable的典型实现
    • BigDecimal、BigInteger 以及所有的数值型对应的包装类:按它们对应的数值大小进行比较
    • Character:按字符的 unicode值来进行比较
    • Boolean:true 对应的包装类实例大于 false 对应的包装类实例
    • String:按字符串中字符的 unicode 值进行比较
    • Date、Time:后边的时间、日期比前面的时间、日期大
      自然排序的过程
  1. 向 TreeSet 中添加元素时,只有第一个元素无须比较compareTo()方法,后面添加的所有元素都会调用compareTo()方法进行比较。
  2. 因为只有相同类的两个实例才会比较大小,所以向 TreeSet 中添加的应该是同一个类的对象。
  3. 对于 TreeSet 集合而言,它判断两个对象是否相等的唯一标准是:两个对象通过 compareTo(Object obj) 方法比较返回值。
  4. 当需要把一个对象放入 TreeSet 中,重写该对象对应的 equals() 方法时,应保 证该方法与compareTo(Object obj) 方法有一致的结果:如果两个对象通过 equals() 方法比较返回 true,则通过compareTo(Object obj) 方法比较应返回 0。 否则,让人难以理解

    3.3.2 定制排序
  5. TreeSet的自然排序要求元素所属的类实现Comparable接口,如果元素所属的没有实现Comparable接口,或不希望按照升序(默认情况)的方式排列元素或希望按照其它属性大小进行排序,则考虑使用定制排序。定制排序,通过Comparato接口来实现。需要重写compare(T o1,T o2)方法。 

  6. 利用int compare(T o1,T o2)方法,比较o1和o2的大小:如果方法返回正整数,则表示o1大于o2;如果返回0,表示相等;返回负整数,表示o1小于o2。 
  7. 要实现定制排序,需要将实现Comparator接口的实例作为形参传递给TreeSet构造器。
  8. 此时,仍然只能向TreeSet中添加类型相同的对象。否则发生ClassCastException异常。
  9. 使用定制排序判断两个元素相等的标准是:通过Comparator比较两个元素返回了0。

    六、Map集合

    一、Map介绍

    1.1 Map的概述

  10. Map和Collection并列存在,用来保存具有映射关系的数据。

    1. map中的key和value可以是任何引用类型的数据。
      1. map中的key必须用set来存放,保证不重复,既同一个map对象的类必须重写hashCode()和equals()方法。
      2. map中的常用实现类。HashMap,linkedHashMap,TreeMap,Properties

        1.2 Map接口继承树

        image.png

        1.3 Map与Collection的区别

  11. Map集合的特点 将键映射到值的对象,一个映射不能 包含重复的键,一个键最多只能映射到一个值

  12. Map与Collection集合的区别:

    1. Map集合存储元素是成对出现的,Map的键是唯一的,值是可以重复的。
    2. Collection集合存储元素是单独出现的,Collection的子接口Set是唯一的,List是可以重复的。
    3. Map集合的数据结构是对键有效的,对值无效
    4. Collection集合的数据结构针对元素有效

      1.4 Map的接口方法

  13. 添加、删除、修改操作:

    1. Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
    2. void putAll(Map m):将m中的所有key-value对存放到当前map中
    3. Object remove(Object key):移除指定key的key-value对,并返回value
    4. void clear():清空当前map中的所有数据
  14. 元素查询的操作:
    1. Object get(Object key):获取指定key对应的value
    2. boolean containsKey(Object key):是否包含指定的key
    3. boolean containsValue(Object value):是否包含指定的value
    4. int size():返回map中key-value对的个数
    5. boolean isEmpty():判断当前map是否为空
    6. boolean equals(Object obj):判断当前map和参数对象obj是否相等
  15. 元视图操作的方法:
    1. Set keySet():返回所有key构成的Set集合
    2. Collection values():返回所有value构成的Collection集合
    3. Set entrySet():返回所有key-value对构成的Set集合

image.png

二、散列表介绍

三、红黑树介绍

七、HashMap

一、HashMap介绍

  • HashMap是map接口中使用频率最高的实现类
  • 允许null键和null值,和set一样,不保证映射的顺序
  • 所有key构成的集合是Set,:无序不可重复的。所以key所在的类要重写hashCode()和equals()
  • 所有value构成 的集合是collection无序的,可以重复的。
  • 一个key-value构成一个entry
  • 所有entry构成的集合是Set:无序的,不可重复的。
  • HahMap判断两个key值相等的标准:两个key通过equals()返回为true,hashCode值也相等。

    二、存储结构图

    2.1 JDK7之前时数组+链表

    image.png

    2.2 JDK8是数组+链表+红黑树

    image.png

  • 当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64时,此时此索引位置上的所数据改为使用红黑树存储。

    • DEFAULT_INITIAL_CAPACITY : HashMap的默认容量,16
    • DEFAULT_LOAD_FACTOR:HashMap的默认加载因子:0.75
    • threshold:扩容的临界值,=容量填充因子:16 0.75 => 12
    • TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树:8
    • MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64
      JDk源码
      image.png

      三、源码分析

      3.1 JDK7版本的源码分析

      image.png
      image.png

      3.2 JDK8版本的源码分析

      image.png
      image.png

      四、HashMap和Hashtable的区别

      共同点
  • 从基本结构和实现上来说基本上都是相同的,都是实现Map接口

区别

  • 同步性
    • HashMap不是同步的
    • Hashtable是同步的
    • 如果想要同步的话可以使用ConcurrentHashMap
  • 是否允许为null
    • HashMap允许为null
    • Hashtable不允许为null
  • contains方法
    • Hashtable有contains方法
    • HashMap将其拆分为containsValue()和containsKey()
  • 继承不同
    • HashMap extends AbstractMap
    • public class Hashtable extends Dictionary

      五、HashMap总结

      八、LinkedHashMap

      一、LinkedHashMap介绍

  1. LinkedHashMap 是 HashMap 的子类
  2. 在HashMap存储结构的基础上,使用了一对双向链表来记录添加元素的顺序
  3. 与LinkedHashSet类似,LinkedHashMap 可以维护 Map 的迭代顺序:迭代顺序与 Key-Value 对的插入顺序一致

    二、类继承图

    image.png

    九、TreeMap

    一、TreeMap介绍

  • TreeMap存储 Key-Value 对时,需要根据 key-value 对进行排序。
  • TreeMap 可以保证所有的 Key-Value 对处于有序状态。
  • TreeSet底层使用红黑树结构存储数据
  • TreeMap 的 Key 的排序: 自然排序:TreeMap 的所有的 Key 必须实现 Comparable 接口,而且所有 的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastException 定制排序:创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对 TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现 Comparable 接口
  • TreeMap判断两个key相等的标准:两个key通过compareTo()方法或 者compare()方法返回0。

    二、类继承图

    image.png

    十、HashTable

  • Hashtable是个古老的 Map 实现类,JDK1.0就提供了。不同于HashMap, Hashtable是线程安全的。

  • Hashtable实现原理和HashMap相同,功能相同。底层都使用哈希表结构,查询速度快,很多情况下可以互用。
  • 与HashMap不同,Hashtable 不允许使用 null 作为 key 和 value
  • 与 HashMap一样,Hashtable 也不能保证其中 Key-Value 对的顺序
  • Hashtable判断两个key相等、两个value相等的标准,与HashMap一致。

    十一、Properties

  • Properties 类是 Hashtable 的子类,该对象用于处理属性文件

  • 由于属性文件里的 key、value 都是字符串类型,所以 Properties 里的 key和 value 都是字符串类型
  • 存取数据时,建议使用setProperty(String key,String value)方法和 getProperty(String key)方法

    十二、Collections工具类

    一、工具类介绍

  • Collections 是一个操作 Set、List 和 Map 等集合的工具类

  • Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作, 还提供了对集合对象设置不可变、对集合对象实现同步控制等方法

    二、操作API

    2.1 排序操作:(均为static方法)

  • reverse(List):反转 List 中元素的顺序

  • shuffle(List):对 List 集合元素进行随机排序
  • sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
  • sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
  • swap(List,int, int):将指定 list 集合中的 i 处元素和 j

    2.2 常用方法

    查找、替换

  • Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素

lObject max(Collection,Comparator):根据 Comparator 指定的顺序,返回
给定集合中的最大元素

  • Object min(Collection)
  • Object min(Collection,Comparator)
  • int frequency(Collection,Object):返回指定集合中指定元素的出现次数
  • void copy(List dest,List src):将src中的内容复制到dest中
  • boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换list
  • Collections 类中提供了多个 synchronizedXxx() 方法,该方法可使将指定集 合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全 问题

    十三、Java集合面试题

    一、Collection和Collections的区别?

  • Collection是集合的一个接口(顶级接口),它提供了对集合对象进行基本操作的通用方法。
    - Collection接口在Java类库中有很多具体的是实现。其作用是为各种集合提供一个最大化的统一操作方式。直接继承的接口有List和Map.

  • Collections是集合类的一个工具类,提供了一系列的静态方法。用于对集合中的元素进行排序、搜索、线程安全等一系列操作。

    二、List、Set、Map之间的区别?

    | 比较 | List | Set | Map | | —- | —- | —- | —- | | 继承接口 | Collection | Collection | | | 常用实现类 | ArrayList,LinkedList, vector | HashSet,LinkedHashSet, TreeSet | HashTable, HashMap | | 常见方法 | | | | | 元素 | 可重复 | 不可重复 | 不可重复 | | 顺序 | 有序 | 无序 | | | 线程安全 | Vector安全 | | HashTable安全 |

三、ArrayList和Vector的区别?

共同点

  • ArrayList都是有序的集合。底层都是数组,允许存储重复的和NULL值。

区别

  • 同步性
    • Vector是同步的
    • ArrayList是不同步的
    • 若是想要集合同步的话,我们也可以使用Collections 中的方法来实现同步ArrayList arraylist = Collections.synchronizedList(new AarrayList(…))
  • 扩容性
    • ArrayList扩容机制为扩大为原来的1.5倍,而Vector扩大为原来的两倍。
  • 扩展 :为什么jdk不推荐使用Vector

    • Vector是线程安全的,所以效率低,类似于stringBuffer
    • Vector空间满了以后,扩容是一倍,而ArrayList是0.5倍
    • 增删只能在尾部进行操作,效率低
    • 给Vector分配内存需要连续的存储空间,如果数据量较大可能会分配失败

      四、HashMap和Hashtable的区别?

      共同点
  • 从基本结构和实现上来说基本上都是相同的,都是实现Map接口

区别

  • 同步性
    • HashMap不是同步的
    • Hashtable是同步的
    • 如果想要同步的话可以使用ConcurrentHashMap
  • 是否允许为null
    • HashMap允许为null
    • Hashtable不允许为null
  • contains方法
    • Hashtable有contains方法
    • HashMap将其拆分为containsValue()和containsKey()
  • 继承不同

    • HashMap extends AbstractMap
    • public class Hashtable extends Dictionary

      五、Set里面的元素是不能重复的,那么是用什么来区分重复与否?是用==还是equals()?

      六、说出ArrayList,LinkedList的存储性能和特性?

  • ArrayList底层是数组,LinkedList底层是链表。

  • ArrayList支持以角标索引出对应的元素,而LinkedList需要遍历出整个链表来搜索出对应的元素,因此 一般来说ArrayList的访问速度要比LinkedList的速度快
  • ArrayList底层数组,对于增加和删除需要通过复制和移动数组来实现。而LinkedList是双向链表增加和删除只需要通过移动指针来实现,消耗是很小的。因此 一般来说LinkedList的增删速度是要比ArrayList的速度快的

    扩展 6.1

  • ArrayList的增删速度未必就比LinkedList慢。

  • 如果增删都是在末尾来操作【每次调用的都是remove()和add()】,此时ArrayList就不需要移动和复制数组来进行操作了。如果数据量有百万级的时,速度是会比LinkedList要快的。(我测试过)
  • 如果删除操作的位置是在中间。由于LinkedList的消耗主要是在遍历上,ArrayList的消耗主要是在移动和复制上(底层调用的是arraycopy()方法,是native方法)。

    • LinkedList的遍历速度是要慢于ArrayList的复制移动速度的
    • 如果数据量有百万级的时,还是ArrayList要快。(我测试过)

      七、Enumeration和Iterator接口的区别?

  • Enumeration是旧的迭代器,现在Iterator替代了它,他是被抛弃的迭代器

  • 主要原因是Iterator比较安全,在集合遍历的时候,它会阻止其他线程去修改集合
  • 区别有三点

    • Iterator的方法名比Enumeration更加科学
    • Iterator有fail-fast机制,更加安全
    • Iterator可以删除元素,而Enumeration不能删除元素

      八、ListIterator有什么特点?

  • ListIterator继承了Iterator接口,用来遍历List集合的元素

  • ListIterator可以实现双向遍历,添加元素,设置元素

    九、并发集合类是什么?

  • Java1.5并发包(java.util.concurrent)包含线程安全集合类,允许在迭代时修改集合

    十、Java中HashMap的key值要是为类对象则该类需要满足什么条件?

  • 因为HashMap的key是Set类型存储的,因此该类必须重写equals()和HashCode()

  • 从源码可以得知,在插入元素的时候是先算出该对象的hashCode。如果hashcode相等话的。那么表明该对象是存储在同一个位置上的。
  • 如果调用equals()方法,两个key相同,则替换元素
  • 如果调用equals()方法,两个key不相同,则说明该hashCode仅仅是碰巧相同,此时是散列冲突,将新增的元素放在桶子上
  • 一般来说,我们会认为:只要两个对象的成员变量的值是相等的,那么我们就认为这两个对象是相等的!因为,Object底层比较的是两个对象的地址,而对我们开发来说这样的意义并不大~这也就为什么我们要重写equals()方法
  • 重写了equals()方法,就要重写hashCode()的方法。因为equals()认定了这两个对象相同,而同一个对象调用hashCode()方法时,是应该返回相同的值的!

    十一、与Java集合框架相关有哪些最好的实践?

  1. 根据需要确定集合的类型。如果是单列的集合,我们考虑用Collection下的子接口ArrayList和Set。如果是映射,我们就考虑使用Map~
  2. 确定完我们的集合类型,我们接下来确定使用该集合类型下的哪个子类~我认为可以简单分成几个步骤:
    • 是否需要同步
      • 去找线程安全的集合类使用
    • 迭代时是否需要有序(插入顺序有序)
      • 去找Linked双向列表结构的
    • 是否需要排序(自然顺序或者手动排序)
      • 去找Tree红黑树类型的(JDK1.8)
  3. 估算存放集合的数据量有多大,无论是List还是Map,它们实现动态增长,都是有性能消耗的。在初始集合的时候给出一个合理的容量会减少动态增长时的消耗~
  4. 使用泛型,避免在运行时出现ClassCastException
  5. 尽可能使用Collections工具类,或者获取只读、同步或空的集合,而非编写自己的实现。它将会提供代码重用性,它有着更好的稳定性和可维护性

    十二、ArrayList集合加入一万条数据,应该怎么提高效率?

    ArrayList的默认初始容量为10,要插入大量数据的时候需要不断扩容,而扩容是非常影响性能的。因此,现在明确了10万条数据了,我们可以直接在初始化的时候就设置ArrayList的容量