:Java 集合框架
Posted 请叫我东子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了:Java 集合框架相关的知识,希望对你有一定的参考价值。
Java 集合框架
1、List(线性结构)
- ArrayList
Object[] 数组实现,默认大小为 10 ,支持随机访问,连续内存空间,插入末尾时间复杂度 o(1),插入第 i 个位置时间复杂度 o(n - i)。扩容,大小变为 1.5 倍,Arrays.copyOf(底层 System.ArrayCopy),复制到新数组,指针指向新数组。 - Vector
类似 ArrayList,线程安全,扩容默认增长为原来的 2 倍,还可以指定增长空间长度。 - LinkedList
基于链表实现,1.7 为双向链表,1.6 为双向循环链表,取消循环更能分清头尾。
2、Map(K,V 对)
-
HashMap
- 底层数据结构,JDK 1.8 是数组 + 链表 + 红黑树,JDK 1.7 无红黑树。链表长度大于 8 时,转化为红黑树,优化查询效率。
- 初始容量为 16,通过 tableSizeFor 保证容量为 2 的幂次方。寻址方式,高位异或,(n-1)&h 取模,优化速度。
- 扩容机制,当元素数量大于容量 x 负载因子 0.75 时,容量扩大为原来的 2 倍,新建一个数组,然后转移到新数组。
- 基于 Map 实现。
- 线程不安全。
-
HashMap (1.7) 多线程循环链表问题
- 在多线程环境下,进行扩容时,1.7 下的 HashMap 会形成循环链表。
- 怎么形成循环链表:
假设有一 HashMap 容量为 2 , 在数组下标 1 位置以 A -> B 链表形式存储。有一线程对该 map 做 put 操作,由于触发扩容条件,需要进行扩容。这时另一个线程也 put 操作,同样需要扩容,并完成了扩容操作,由于复制到新数组是头部插入,所以 1 位置变为 B -> A 。这时第一个线程继续做扩容操作,首先复制 A ,然后复制 B ,再判断 B.next 是否为空时,由于第二个线程做了扩容操作,导致 B.next = A,所以在将 A 放到 B 前,A.next 又等于 B ,导致循环链表出现。
-
HashTable
- 线程安全,方法基本全用 Synchronized 修饰。
- 初始容量为 11 ,扩容为 2n + 1 。
- 继承 Dictionary 类。
-
ConcurrentHashMap
- 线程安全的 HashMap。
- 1.7 采用分段锁的形式加锁;1.8 使用 Synchronized 和 CAS 实现同步,若数组的 Node 为空,则通过 CAS 的方式设置值,不为空则加在链表的第一个节点。获取第一个元素是否为空使用 Unsafe 类提供的 getObjectVolatile 保证可见性。
- 对于读操作,数组由 volatile 修饰,同时数组的元素为 Node,Node 的 K 使用 final 修饰,V 使用 volatile 修饰,下一个节点也用 volatile 修饰,保证多线程的可见性。
-
LinkedHashMap
- LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,
- LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。
-
TreeMap有序的 Map,红黑树结构,可以自定义比较器来进行排序。
-
Collections.synchronizedMap 如何实现 Map 线程安全?基于 Synchronized ,实际上就是锁住了当前传入的 Map 对象。
3、Set(唯一值)
- HashSet 基于 HashMap 实现,使用了 HashMap 的 K 作为元素存储,V 为 new Object() ,在 add() 方法中如果两个元素的 Hash 值相同,则通过 equals 方法比较是否相等。
- LinkedHashSet 继承于 HashSet,并且其内部是通过 LinkedHashMap 来实现的。
- TreeSet 红黑树实现有序唯一。
以上是关于:Java 集合框架的主要内容,如果未能解决你的问题,请参考以下文章