跳转至

第14章 集合(P499 - P553)

1、集合框架体系

  • 单列集合

image-20240308095853250

image-20240308100135851

  • 双列集合

image-20240308095939329

image-20240308100334650

2、单列集合

1、Collection接口和常用方法

  • collection接口实现特点

public interface Collection<E\> extend Iterable<E\>

  1. collection 实现子类可以存放多个元素,每个元素可以是Object
  2. 有些Collection的实现类,可以存放重复元素,有些不可
  3. 有些Collection的实现类,有些是有序的(List),有些不是(Set)
  4. Collection 接口没有直接的实现子类,是通过它的子接口Set和List来实现的

  5. 常用方法

  6. add(); 添加单个元素

  7. remove(); 删除指定元素
  8. contains(); 查找元素是否存在
  9. size(); 获取元素个数
  10. isEmpty(); 判断是否为空
  11. clear(); 清空
  12. addAll(); 添加多个元素
  13. containsAll(); 查找多个元素是否都存在
  14. removeAll(); 删除多个元素

2、Collection 接口便利元素方式

  • 使用 Iterator (迭代器)

  • Iterator 对象称为迭代器,主要用于遍历 Collection 集合中的元素

  • 所有实现了Collection 接口的集合类都有一个 iterator()方法,用以返回一个实现了Iterator 接口的对象,即可以返回一个迭代器

  • Iterator 的结构图

    image-20240308103317529

    注意:在调用iterator.next() 方法之前必须要调用iterator.hasNext() 进行检测,若不调用,并且下一条记录无效,直接调用iterator.next() 会抛出NoSuchElementException 异常。

  • Iterator 仅用于遍历集合,Iterator 本省并不存放对象

  • 当再次使用迭代器时,需要重置迭代器

    iteraator = col.ietrator();

快捷键:itit

显示所有快捷键的快捷键:Ctrl + j

  • 增强 for 循环

增强for循环,可以代替iterator迭代器,特点:增强 for 就是简化版的iterator,本质一样。只能用于遍历集合数组

  • 基本语法

    for (元素类型 元素名: 集合名或数组名) {

    ​ 访问元素

    }

  • 快捷键:I

3、List接口

  • 基本介绍

List 接口时 Collection 接口的子接口

  1. List 集合类中所有元素有序(即添加顺序和取出顺序一致)、且可重复

  2. List 集合中的每个元素都有相应的顺序索引,即支持索引

  3. List 容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素

    list.get(3)

  4. JDK API 中 List 接口的实现类有

    image-20240308121643737

    常用的有:ArrayList、LinkedList、Vector

  5. 常用方法

  6. void add(int index,Object ele):在index位置插入ele元素

  7. boolean addAll(int index,Collection eles):从index位置开始将eles中所有元素添加进来
  8. Object get(int index):获取指定index位置的元素
  9. int indexOf(Object obj):返回obj在当前集合中首次出现的位置
  10. int lastIndexOf(Object obj):返回obj在当前集合中最后一次出现的位置
  11. Object remove(int index):移除指定index位置的元素,并返回此元素
  12. Object set(int index,Object ele):设置指定index位置的元素为ele,相当于是替换
  13. List subList(int fromIndex,int toIndex):返回从fromIndex到toIndex位置的子集合 [fromIndex,toIndex)

1、ArrayList

  • ArrayList 的底层操作机制源码分析

  • ArrayList中维护了一个Object类型的数组elementData

    transient Object[] elementData;//transient 表示瞬间,短暂的,表示该属性不会被序列化

  • 当创建ArrayList对象时,如果使用的时无参构造器,则初始elementData容量为0,第一次添加,则扩容elementData为10,如果需要再次扩容,则扩容elementData为1.5倍

  • 如果使用的是直接指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍

2、Vector底层结构和源码刨析

  • 基本介绍

  • Vector 类定义说明

    image-20240308153019910

  • Vector 底层也是一个对象数组,protected Object[] elementData;

  • Vector 是线程同步的,即线程安全,Vector类的操作方法带有 synchronied

  • 在开发中,需要线程同步安全时,考虑使用Vector

  • 扩容机制

如果是无参,默认10,满后,就按2倍扩容

如果指定大小,则每次直接按2倍扩容

3、LinkedList底层结构

  • 说明

  • LinkedList 底层实现了双向链表和双端队列的特点

  • 可以添加任意元素

  • 线程不安全,没有实现同步

  • LinkedList 的底层操作机制

  • LinkedList 底层维护了一个双向链表
  • LinkedList 中维护了两个属性first和last分别指向首节点和尾节点
  • 每个节点(Node对象),里面有维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点。最终实现双向链表
  • 所以LinkedList 的元素的添加删除,不是通过数组完成的,相对来说效率较高

4、ArrayList和LinkedList的比较

底层结构 增删效率 改查效率
ArrayList 可变数组 较低
数组扩容
较高
LinkedList 双向链表 较高
通过链表追加
较低
  • 如何选择
  • 改查操作多,选择ArrayList
  • 增删比较多,选择LinkedList
  • 一般来说,在程序中,80%-90%都是查询,因此大部分情况下会选择ArrayList
  • 在一个项目中,根据业务 灵活选择,也可能是这样,一个模块使用的是ArrayList,另一个模块是LinkedList

4、Set接口

  • Set接口基本介绍

  • 无序(添加和取出顺序不一致),没有索引

  • 不允许重复元素,所以最多包含一个null

  • JDK API 中Set接口的实现类有:

    image-20240308163636243

  • Set接口的常用方法

和List接口一样,Set接口也是Collection子接口,因此常用方法和Collection接口一样

  • Set接口的遍历方式

同Collection的遍历方式一样

不能用索引的方式来获取

1、HashSet

  1. HashSet实现了Set接口
  2. HashSet实际上是HashMap
  3. 可以存放null值,但是只能有一个null
  4. HashSet不保证元素是有序的,取决于hash后,在确定索引的结果
  5. 不能有重复的元素/对象

  6. HashSet 底层机制

HashSet 底层是(数组 + 链表 + 红黑树)

  1. HashSet 底层是 HashMap
  2. 添加存储数据表table,看这个索引位置是否已经存放元素
  3. 如果没有,则直接加入
  4. 如果有,调用equals 比较,如果相同,就放弃添加,如果不同,则添加到最后
  5. 在Java8 中,如果一条链表的元素个数到达TREEIFYTHRESHOLD(默认是8),并且table的大小 >= MINTREEIFY_CAPACITY(默认64)就会进行树化(红黑树)

  6. 扩容机制

  7. HashSet底层是HashMap,第一次添加时,table 数组扩容到16,临界值(threshold)是16*加载因子(loadFactor)是0.75 = 12

    依照的是size(每加入一个元素,size 就会加 1)

  8. 如table 数组使用到了临界值 12,就会扩容到16*2=32,新的临界值就是32*0.75=24,以此类推

  9. 在Java8 中,如果一条链表的元素个数到达TREEIFYTHRESHOLD(默认是8),并且table的大小 >= MINTREEIFY_CAPACITY(默认64)就会进行树化(红黑树)

2、LinkedHashSet

  • LinkedHashSet说明
  • LinkedHashSet是HashSet的子类
  • LinkedHashSet 底层是一个LinkedHashMap,底层维护了一个数组 + 双向链表(LinkedHashSet 有 head 和 tail)
  • LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的
  • LinkedHashSet 不允许添加重复元素
  • 每个结点有 befor 和 after 属性,这样可以形成双向链表
  • 在添加一个元素使,先求hash值,在求索引,确定该元素在hashtable的位置,然后添加的元素加入到双向链表(如果已经存在,不添加【原则和hashset一样】)
  • 确保遍历LinkedHashSet 插入顺序和遍历顺序一致

5、Map接口

image-20240309113218542

  • Map接口实现类特点
  • Map与Collection并列存在。用于保存具有映射关系的数据:Key - Value(双列元素)

  • Map 中的 Key 和 value 可以是任何引用类型的数据,会封装到 HashMap$Node 对象中

  • Map 中的 key 不允许重复(当有相同的 key ,就会替换),原因和 HashSet 一样

  • Map 中的 value 可以重复

  • Map 的 key 可以为 null ,value 也可以为 null,注意 key 为 null,只能有一个,value 为 null ,可以多个

  • 常用 String 类作为 Map 的 key

  • key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到对应的 value

  • Map存放数据的 key-value示意图,一对 k-v 是放在一个Node中的,因为 Node 实现了 Entry 接口

    image-20240309141645427

  • 解读

  • k-v 最后是HashMap$Node node = newNode(hash,key,value,null)

  • k-v 为了方便遍历,还创建 EntrySet 集合,该集合存放的元素的类型 Entry ,而一个 Entry 对象就有 k,v EntrySet> 即: transient Set> entrySet;
  • entrySet 中,定义的类型是 Map.Entry,但实际上存放的还是 HashMap$Node ,因为 static class Node implements Map.Entry
  • 当把 HashMap$Node 对象,存放到 entrySet 就方便我们遍历,因为 Map.Entry 提供了方法 K getKey(); V getValue();

  • Map接口常用方法

  • put:添加

  • remove:根据键(key)删除映射关系
  • get:根据键获取值
  • size:获取元素个数
  • isEmpty:判断个数是否为0
  • clear:清除
  • containsKey:查找键是否存在

  • Map接口遍历方法

  • containsKey:查找键是否存在

  • keySet:获取所有的键
  • entrySet:获取所有关系
  • vlaues:获取所有的值

  • HaspMap小结

  • HashMap 使Map 接口使用频率最高的实现类

  • HaspMap没有实现同步,因此线程是不安全的 ,方法没有做同步互斥的操作

  • HashMap底层机制及源码

  • 扩容机制

    1. HashMap 底层维护了Node 类型的数组table ,默认为null
    2. 当创建对象时将加载因子(loadfactor)初始化为0.75
    3. 当添加key-val时,通过key的哈希值得到在table的索引。然后 判断该索引处是否有元素,如果没有元素直接添加。如果该索引处有元素,继续判断该元素的key是否和准备加入的key相等,如果相等,则直接替换val;如果不相等需要判断是树结构还是链表结构,做出相应的处理。如果添加时发现容量不够,则需扩容
    4. 第一次添加,则需要扩容table容量为16,临界值(threshold)为12
    5. 以后再扩容,则需要扩容table容量为原来的2倍,临界值为原来的2倍,及24,以此类推
    6. 再java8 中,如果一条链表的元素个数超过 TREEIFYTHRESHOLD(默认是 8),并且 table 的大小 >= MINTREEIFY_CAPACITY(默认64),就会进行树化(红黑树)

1、HashTable

  • HashTable 基本介绍
  • 存放的元素是键值对:即 K - V
  • hashTable的键和值都不能为null
  • hashTable 使用的方法基本上和HashMap一样
  • hashTable 是线程安全的,hashMap 是线程不安全的
  • HashTable的底层
  • 底层有数组 HashTable$Entry[] 初始化大小为 11
  • 临界值 threshold 8 = 11 * 0.75
  • 按照 int newCapacity = (oldCapacity << 1) + 1; 的大小扩容

2、Properties

  • 基本介绍
  • Properties类继承自HashTable类并实现了Map接口,也是使用一种键值对的形式来保存数据
  • 使用特点和HashTable类似
  • Properties 还可以用于 从 xxx.properties 问文件中,加载数据到Properties 类对象,并进行读取和修改,(xxx.properties 文件通常作为配置文件)

3、总结——开发中如何选择集合实现类

  • 先判断存储的类型(一组对象【单列】 或 一组键值对【双列】

  • 一组对象【单列】:Collection接口

    • 允许重复:List
    • 增删改查多:LinkedList【底层 维护了一个双向链表】
    • 改查多:ArrayList【底层维护了 Object类型的可变数组】
    • 不允许重复:Set
    • 无序:HashSet【底层是HashMap,维护了一个哈希表,即(数组 + 链表 红黑树)】
    • 排序:TreeSet
    • 插入和取出顺序一致:LinkedHashSet,维护了数组 + 双向链表
    • 一组键值对【双列】:Map
    • 键无序:HashMap【底层是:哈希表 jdk7:数组 + 链表,jdk8:数组 + 链表 + 红黑树】
    • 键排序:TreeMap
    • 键插入和取出顺序一致:LingkedHashMap
    • 读取文件:Proerties

4、Collection工具类

  • collection 工具类介绍
  • Collection 是一个操作 Set、List 和 Map 等集合的工具类
  • Collection 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作
  • 排序操作:(均为static方法)
  • reverse(List):反转List中元素顺序
  • shuffle(Lise):对List 集合元素进行随机排序
  • sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
  • sort(List,Comparator):根据指定的 Comparator 产生的顺序对List 集合元素进行排序
  • swap(List,int,int):将指定List 集合中的 i 出元素和 j 处元素进行交换
  • 查找、替换
  • Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
  • Object 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 对象的所有旧值