JAVA集合
大纲
#mermaid-svg-NABUCzGFVm2pmvhu {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-NABUCzGFVm2pmvhu .error-icon{fill:#552222;}#mermaid-svg-NABUCzGFVm2pmvhu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NABUCzGFVm2pmvhu .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-NABUCzGFVm2pmvhu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NABUCzGFVm2pmvhu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NABUCzGFVm2pmvhu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NABUCzGFVm2pmvhu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NABUCzGFVm2pmvhu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NABUCzGFVm2pmvhu .marker.cross{stroke:#333333;}#mermaid-svg-NABUCzGFVm2pmvhu svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NABUCzGFVm2pmvhu .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-NABUCzGFVm2pmvhu .cluster-label text{fill:#333;}#mermaid-svg-NABUCzGFVm2pmvhu .cluster-label span{color:#333;}#mermaid-svg-NABUCzGFVm2pmvhu .label text,#mermaid-svg-NABUCzGFVm2pmvhu span{fill:#333;color:#333;}#mermaid-svg-NABUCzGFVm2pmvhu .node rect,#mermaid-svg-NABUCzGFVm2pmvhu .node circle,#mermaid-svg-NABUCzGFVm2pmvhu .node ellipse,#mermaid-svg-NABUCzGFVm2pmvhu .node polygon,#mermaid-svg-NABUCzGFVm2pmvhu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NABUCzGFVm2pmvhu .node .label{text-align:center;}#mermaid-svg-NABUCzGFVm2pmvhu .node.clickable{cursor:pointer;}#mermaid-svg-NABUCzGFVm2pmvhu .arrowheadPath{fill:#333333;}#mermaid-svg-NABUCzGFVm2pmvhu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NABUCzGFVm2pmvhu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NABUCzGFVm2pmvhu .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-NABUCzGFVm2pmvhu .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-NABUCzGFVm2pmvhu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NABUCzGFVm2pmvhu .cluster text{fill:#333;}#mermaid-svg-NABUCzGFVm2pmvhu .cluster span{color:#333;}#mermaid-svg-NABUCzGFVm2pmvhu div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-NABUCzGFVm2pmvhu :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;}集合CollectionListArrayList,基于动态数组Vector,和ArrayList类似,但是线程安全LinkedList,基于双向链表QueueArrayDeque,可扩容动态双向数组LinkedList,可以用于实现双向队列PriorityQueue,基于堆结构DelayQueue,一个内部依靠AQS队列同步器所实现的无界延迟阻塞队列MapSetTreeSet,基于红黑树,有序性HashSet,基于哈希表LinkedHashSet,基于双向链表Map容器TreeMap,基于红黑树HashMap,基于哈希表HashTable,和HashMap类似,但是线程安全,它是遗留类,不应该去使用它ConcurrentHashMap,效率高.引入分段锁LinkedHashMap,双向链表fail-fast机制
DelayQueue
建议学习ing
HashSet
- HashSet 底层就是基于 HashMap 实现的
- 在 JDK1.8 中,实际上无论HashSet中是否已经存在了某元素,HashSet都会直接插入,只是会在add()方法的返回值处告诉我们插入前是否存在相同元素
HashMap
- HashMap 可以存储 null 的 key 和 value,但 null 作为键只能有一个,null 作为值可以有多个
- HashMap 默认的初始化大小为 16。之后每次扩充,容量变为原来的 2 倍。
- HashMap 会将其扩充为 2 的幂次方大小(HashMap 中的tableSizeFor()方法保证)
- HashMap 对哈希值进行了高位和低位的混合扰动处理以减少冲突
- 当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树
- JDK1.7 及之前版本的 HashMap 在多线程环境下扩容操作可能存在死循环问题,这是由于当一个桶位中有多个元素需要进行扩容时,多个线程同时对链表进行操作,头插法可能会导致链表中的节点指向错误的位置,从而形成一个环形链表,进而使得查询元素的操作陷入死循环无法结束。
JDK1.8 版本的 HashMap 采用了尾插法而不是头插法来避免链表倒置。
HashTable
- Hashtable 不允许有 null 键和 null 值,否则会抛出 NullPointerException
- 创建时如果不指定容量初始值,Hashtable 默认的初始大小为 11,之后每次扩充,容量变为原来的 2n+1
- Hashtable 直接使用键的 hashCode() 值
- 使用 synchronized 来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低。
ConcurrentHashMap
多线程下无法正确判定键值对是否存在(存在其他线程修改的情况),单线程是可以的(不存在其他线程修改的情况)
JDK1.7中ConcurrentHashMap 对整个桶数组进行了分割分段(Segment,分段锁),每一把锁只锁容器其中一部分数据(下面有示意图),多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。
JDK1.8 的时候,ConcurrentHashMap 已经摒弃了 Segment 的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作。
ConcurrentHashMap 的 key 和 value 不能为 null 主要是为了避免二义性。null 是一个特殊的值,表示没有对象或没有引用。如果你用 null 作为键,那么你就无法区分这个键是否存在于 ConcurrentHashMap 中,还是根本没有这个键。同样,如果你用 null 作为值,那么你就无法区分这个值是否是真正存储在 ConcurrentHashMap 中的,还是因为找不到对应的键而返回的。
多线程环境下,存在一个线程操作该 ConcurrentHashMap 时,其他的线程将该 ConcurrentHashMap 修改的情况,所以无法通过 containsKey(key) 来判断否存在这个键值对,也就没办法解决二义性问题了。
原子操作:putIfAbsent、compute、computeIfAbsent 、computeIfPresent、merge
fail-fast机制
多个线程对集合进行修改的时候,可能会抛出ConcurrentModificationException。