java集合

注: 本文谈论的是基于JDK1.8版本

脑图

  • ArrayList的底层数据结构:数组
  • LinkedList的底层数据结构:链表。既实现了List接口,又实现了Queue接口,在使用的时候,如果我们把它当作List,就获取List的引用,如果我们把它当作Queue,就获取Queue的引用
  • CopyOnWriteArrayList:和ArrayList基本一模一样,但它是线程安全
  • HashMap,ConcurrentHashMap 的底层数据结构: 数组+链表+红黑树实现,插入数据方式为尾插法,当链表长度超过阈值(8)时,并且数组长度超过64,将链表转换为红黑树,如果没超过64,只扩容(重新计算哈希值,复制数组过去),并不会转成红黑树,基于HashMap实现
  • LinkedHashMap:底层是数组+链表+红黑树+双向链表
  • TreeSet,TreeMap的底层数据结构:红黑树
  • HashSet的底层数据结构:数组+链表+红黑树

List接口

  • 元素有序
  • 有索引
  • 元素可以重复

ArrayList

  • ArrayList的底层就是数组。
  • 实现了Random Access接口,查询快;但是因为插入数据要更改其他元素位置,所以插入慢
  • 如何扩容? 创建一个新的数组,再将旧数组复制进去,这样长度就增加了
  • 当第一次add元素的时候,初始化默认大小为10,每次扩容为原来的1.5倍

LinkedList

  • 底层是链表。链表增删快,故LinkedList常用来增删数据.
  • 是一个双向链表,所以没有容量,没有扩容,直接在头尾添加即可.

CopyOnWriteArrayList

  • 和ArrayList基本一模一样,但它是线程安全的
  • 适合多读少写的场景
  • 任何对array在结构上有所改变的操作(add、remove、clear等),CopyOnWriterArrayList都会copy现有的数据,再在copy的数据上修改,这样就不会影响COWIterator中的数据了,不会抛ConcurrentModificationException异常(对set,add没有作用,因为set,add本来就要加锁),修改完成之后改变原有数据的引用即可。
  • 读操作不加锁,写操作加锁,在进行add,set等操作时,会通过ReentrantLock进行加锁
  • 缺点:
    • 1.复制的数组会消耗内存
    • 2.不能读取实时性的数据
    • 3.会产生大量的对象

Set接口

使用了HashMap的key,value为空

  • 元素无序
  • 没有索引
  • 元素不能重复

Collection遍历删除元素

1.普通for循环:注意每次删除之后索引要--
2.Iterator遍历:不过要使用Iterator类中的remove方法,如果用List中的remove方法会报错
3.增强for循环foreach不能删除,强制用List中的remove方法会报错.foreach循环实际上是生成器

Map接口

HashMap

  • 键值对
  • 无序,不支持排序
  • 采用哈希算法定位元素,计算公式为:通过hashCode()的高16位异或低16位(h = k.hashCode()) ^ (h >>> 16)得到h,然后 h&(length - 1),k是键,length为数组长度.所以length要设置2的n次方,这样可以散列的更均匀,而且计算下标方便,并且扩容的时候可以让原数组处的链表长度减小一半(理想情况下)
  • key,value可以存null
  • value可以重复,key重复会覆盖value
  • 数组默认大小是16,存储的数据超过容量的75%开始扩容.默认大小和负载因子可以自定义.扩容后容量是之前的两倍
  • 多线程使用时,扩容死循环,要用ConcurrentHashMap

ConcurrentHashMap

  • 线程安全的HashMap,但是key,value不允许null
  • 相比Hashmap多了锁对象segment
  • put基本流程
    • 通过CAS+synchronized加锁(jdk1.8),只需要锁住数组中有链表元素的头部位置;1.7是分段加锁
    • 1.如果没有hash冲突就直接通过CAS插入
    • 2.如果有hash冲突或者CAS操作失败,说明存在并发情况,使用synchronized加锁
    • 3.如果插入成功就调用addCount()方法统计size,并且检查是否需要扩容

LinkedHashMap

  • 比HashMap多维护了一个链表用来记录顺序
  • LinkedHashMap 可以通过构造参数 accessOrder 来指定双向链表是否在元素被访问后改变其在双向链表中的位置。
  • LRU实现:插入数据后对调用afterNodeInsertion,afterNodeInsertion()方法中会调用removeEldestEntry,如果removeEldestEntry(first)返回true,按照LRU策略,那么会删除头节点(注意这里删除的是头节点!!!所以每次访问元素或者插入元素之后都要将该元素放到链表末尾)。这个也是实现LRU的关键点!!!!!

HashTable

为了解决哈希冲突.不建议使用,完全可以用ConcurrentHashMap替代

哈希表可以用来高效率解决元素不可重复这个问题,其本质就是:数组+链表+红黑树。
所以如果新建了一个对象,需要重写hashCode方法和equals方法

  • key,value都不可为null
  • 线程安全,所有方法上都加了synchronized关键字
  • 哈希值就有点类似于数组中的索引,因为哈希值不同其元素必定不同
    • 如果没有相同的哈希值,直接添加进集合
    • 如果有相同的哈希值,再用equals方法比较内容是否一样
  • 扩容
    • 哈希表中数组默认长度11,如果数组中的元素超过了75%就开始扩容
    • 扩容时,capacity=2*capacity+1
    • 如果链表元素数量超过8,就将链表重构成红黑树。

SortedMap接口

  • 有序
  • 支持排序
  • 不可存null
  • 查找较慢
  • value可以重复
  • 采用红黑树排序,可以调用Comparator类型的构造方法进行定制排序
  • TreeMap是它的实现类,继承体系 Map -> SortMap -> NavigbleMap -> TreeMap
posted @ 2021-03-22 17:12  rm-rf*  阅读(52)  评论(0编辑  收藏  举报