Java集合框架体系

Collection

Map

Java集合类主要由两个接口Collection和Map派生出来的,Collection有三个子接口:List、Set、Queue。

  • Collection:最基本的集合接口,代表一组元素的集合。
    • List:代表有序的、可重复的元素。
    • Set:代表不可重复的的集合。
    • Queue: 代表队列
  • Map:存储键值对的集合,键不允许重复。

List 、Set、Queue、Map的区别

  • List:

    • 基于索引的集合,可以包含重复的元素。
    • 支持元素的有序存储,即元素插入的顺序就是它们在列表中的顺序。
    • 允许对元素进行快速随机访问。
    • 常见的实现类有 ArrayList、LinkedList
  • Set:

    • 不包含重复元素的集合。
    • 通常不保证元素的顺序,或者保证元素按照某种规则排序(如 TreeSet)。
    • HashSet 是基于哈希表实现的,不保证元素顺序;LinkedHashSet 维护元素插入的顺序;TreeSet 则根据元素的自然顺序或构造时提供的比较器对元素进行排序。
    • 常见的实现类有 HashSet、LinkedHashSet 和 TreeSet。
  • Queue:

    • 队列是一种特殊的集合,主要用于维护元素的顺序。通常按照先进先出(FIFO)的原则进行元素的插入和移除。
    • 支持元素的插入和移除操作,如 offer、poll、peek 等
    • 常见的实现类有LinkedList(先进先出FIFO)、PriorityQueue(优先队列)和 ArrayDeque。
  • Map

    • 存储键值对的集合,每个键最多只能映射到一个值。
    • 键必须唯一,而值可以重复。
    • 不保证元素的顺序,或者可以按照键或值的某种规则排序(如 TreeMap)。
    • 常见的实现类有 HashMap、LinkedHashMap、TreeMap

ArrayList与LinkedList的区别

1.底层数据结构:ArrayList基于动态数组实现;LinkedList基于(双向)链表实现。
2.随机索引访问:ArrayList的速度要优于LinkedList。因为ArrayList直接通过数组下标直接找到元素;LinkedList要移动指针遍历每个元素直到找到为止。
3.新增和删除元素:LinkedList的速度要优于ArrayList。因为ArrayList在新增和删除元素时,可能移动其它元素、扩容和复制数组;LinkedList除了实例化对象需要时间外,只需要修改指针即可。(LinkedList新增删除元素比ArrayList快的两个前提:1 往集合中间插入数据时ArrayList比linkedList慢 2 ArrayList正好扩容的时候添加数据要比LinkedList慢)
4.内存占用:ArrayList占用比LinkedList小,因为LinkedList的每个元素都需要存储额外的指针(prev 和 next)
5.线程安全:ArrayList与LinkedList都是非同步的,说明是非线程安全的,

特性 ArrayList LinkedList
内部结构 动态数组(基于数组实现) 双向链表(基于链表实现)
随机访问 快(O(1) 时间复杂度) 慢(O(n) 时间复杂度,需要遍历链表)
添加/删除 可能慢(特别是需要扩容时) 快(O(1) 时间复杂度,但在链表中间较慢)
内存占用 相对较小 相对较大(每个元素都需要额外的内存来存储指针)
扩容机制 当数组容量不足时自动扩容(JDK8,1.5倍) 不需要扩容
迭代器 快速且轻量级 相对较慢且占用更多内存
适用场景 频繁的随机访问 频繁的插入和删除操作

Arraylist 和 Vector 的区别

ArrayList在内存不够时,默认是1.5被扩展,Vector是默认扩展1倍。
ArrayList是非同步的。Vector是同步的,属于线程安全级别的。由于 Vector 的同步机制,它在性能上通常比 ArrayList 慢。
通常建议使用 ArrayList,因为它提供了更好的性能,并且在需要线程安全时,可以通过Collections.synchronizedList() 方法或使用 CopyOnWriteArrayList 类来实现。

HashMap

HashMap是一个散列表,它存储的内容是键值对(key-value)映射,根据键的HashCode值存储数据。HashMap 使用数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的, 链表长度大于8(TREEIFY_THRESHOLD)时,会把链表转换为红黑树,红黑树节点个数小于6(UNTREEIFY_THRESHOLD)时才转化为链表,防止频繁的转化。

示例:
hashcode是int型。提前了解,十进制转二进制,位运算符。

假设
第一步:h=10,
第二步:hash=10^(10>>>16)。

>>>是无符号右移运算。10的二进制是0000000000000000 0000000000001010。那么10>>>16,右移16位意味着丢弃最低的16位,结果是0。
^是异或运算,10^0结果还是10。

第三步:i = (n - 1) & hash

&是与运算。n默认是16。i=(16-1)&10结果是10。

因为第二步是取hashcode丢弃最低的16位的结果与hashcode进行按位异或运算,所以又被称为高16位运算
因为10&(16-1)=10%16,所以第三步又被称为取模运算
计算机中直接求余效率不如位移运算,hash%length==hash&(length-1)的前提是length是2的n次方,能均匀分布减少碰撞。

源码解析可以查看HashMap的put方法

  • 扩容机制(请阅读HashMap的resize方法)

    • 数组扩容
      当hashmap中的元素超过阈值(当前容量与负载因子的乘积),触发扩容。
      通过创建一个2倍当前容量的新数组,替换原数组来完成扩容。
      在此过程中需要重新计算每个键的哈希值,键值对需要重新映射

    • 链表扩容
      当链表长度超过一定阈值(TREEIFY_THRESHOLD,默认为8)时,链表会被转换为红黑树。
      当红黑树节点个数小于6(UNTREEIFY_THRESHOLD)时,红黑树会被转化为链表。

  • 红黑树

    红黑树是一种自平衡的二叉查找树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。它具有以下特性:

    1.节点颜色:每个节点要么是红色,要么是黑色。
    2.根节点:根节点是黑色的。
    3.叶子节点:所有叶子节点(NIL节点,空节点)都是黑色的。
    4.红色节点的子节点:每个红色节点的两个子节点都是黑色的(不能有两个连续的红色节点)。
    5.路径黑色节点数:从根节点到任意叶子节点的路径上,黑色节点的数量相同。

    可结合HashMap里的TreeNode类理解。

HashMap和HashTable的区别

HashMap 和 Hashtable 都是 Java 中用于存储键值对的集合类,以下是它们的一些区别:

  • 继承关系:

    • Hashtable 继承自 Dictionary 类,而 Dictionary 类是 Java 的早期版本中的一个类,现在已经不推荐使用
    • HashMap 继承自 AbstractMap 类
  • 线程安全性:

    • Hashtable 是线程安全的,它的所有公共方法都是同步的,这意味着在多线程环境中,Hashtable 可以安全地使用而不需要额外的同步措施。
    • HashMap 是非线程安全的,如果在多线程环境中使用,需要外部同步或者使用 Collections.synchronizedMap 方法来包装它。
  • null 键和值:

    • Hashtable 不允许使用 null 作为键或值。
    • HashMap 允许使用一个 null 键和多个 null 值。

在实际应用中,由于 HashMap 提供了更好的性能和更灵活的使用方式,它通常是首选的 Map 实现。如果需要线程安全的 Map 实现,可以使用 ConcurrentHashMap,它是 Java 并发包中的一个线程安全 Map 实现,或者使用 Collections.synchronizedMap 方法来包装一个 HashMap。

HashMap与ConcurrentHashMap的区别

ConcurrentHashMap 和 HashMap 都用于存储键值对,以下是它们的以下区别:

  • 线程安全性

    • ConcurrentHashMap 是线程安全的
    • HashMap 是非线程安全的

HashSet

HashSet 实现了 Set 接口,它不允许集合中有重复的元素,并且不保证集合的迭代顺序;

以下是 HashSet 的一些关键特性:

  • 不允许重复

    HashSet 保证集合中每个元素都是唯一的。尝试添加已存在的元素将不会被添加。

  • 无序

    HashSet 不保证元素的顺序,这意味着每次迭代 HashSet 时元素的顺序可能不同。

  • 基于哈希表

    HashSet 内部使用哈希表实现,这使得添加、删除和查找元素的操作通常具有常数时间复杂度(即 O(1))。

  • 非同步

    HashSet 是非线程安全的。在多线程环境中,如果需要线程安全,可以使用 Collections.synchronizedSet 方法将其包装,或者使用 ConcurrentHashMap 作为替代。

当需要迭代顺序一致时,不应该使用 HashSet,而应该使用 LinkedHashSet。

TreeSet

TreeSet 实现了 Set 接口,它不允许集合中有重复的元素,并且保证了元素处于排序状态。TreeSet 基于红黑树(一种自平衡的二叉搜索树)实现,这使得它能够提供有序的数据集合。

以下是 TreeSet 的一些关键特性和用法:

  • 不允许重复

    TreeSet 不允许插入重复的元素。如果尝试添加已存在的元素,该元素不会被添加。

  • 有序

    TreeSet 保证了元素的迭代顺序是有序的,这使得它适合需要有序数据的场景。

  • 性能:

    TreeSet 的查找、插入和删除操作通常具有 O(log n) 的时间复杂度,其中 n 是集合中的元素数量。

  • 线程安全性:

    TreeSet 是非线程安全的。在多线程环境中,如果需要线程安全,可以使用 Collections.synchronizedSortedSet 方法将其包装,或者使用 ConcurrentSkipListSet。

TreeMap

TreeMap 实现了 Map 接口,它存储键值对并保证了键处于排序状态。TreeMap 基于红黑树(一种自平衡的二叉搜索树)实现,这使得它能够提供有序的数据集合。

以下是 TreeMap 的一些关键特性和用法:

  • 不允许重复键

    TreeMap 不允许插入重复的键。如果尝试添加已存在的键,那么旧的值将被新的值替换。

  • 有序

    TreeMap 保证了键处于有序状态,并且保证了元素的迭代顺序是有序的,这使得它适合需要有序数据的场景。

  • 性能:

    TreeMap 的查找、插入和删除操作通常具有 O(log n) 的时间复杂度,其中 n 是映射中的元素数量。

  • 线程安全性:

    TreeMap 是非线程安全的。在多线程环境中,如果需要线程安全,可以使用 Collections.synchronizedMap 方法将其包装。

参考

posted @ 2024-09-06 15:53  抒写  阅读(10)  评论(0编辑  收藏  举报