Collection
由List,Set,Queue/Deque组成
List
有序、可重复
实现类有Vector,ArrayList,LinkedList
Vector
数组、查询快、增删慢、线程安全、效率低
线程安全的动态数组,会根据需要自动扩展容量,当到达数组容量上限时会新建数组,并复制原数组
每次扩容扩1倍
ArrayList
数组、查询快、增删慢、非线程安全、效率高
最常用的动态数组,由于不是线程安全的,所以性能比Vector更好,内部容器同样是使用数组
每次扩容扩50%
尾部删很高效
LinkedList
链表、查询慢、增删快、非线程安全、效率高
双向链表,不需要扩容操作,也是非线程安全的
使用
Vector和ArrayList作为动态数组,内部元素是顺序存储的,适合随机访问的场景。
除了在尾部插入/删除数据外,其他操作的性能比LinkedList稍差。比如在集合中间插入元素时,后面所有元素都需要向后移
Set
无序、不可重复
实现类有HashSet,LinkedHashSet,TreeSet
Set是不允许元素重复的,这是和List的主要区别,集合内不会出现两个equals=true的对象
TreeSet
红黑树、有序
封装TreeMap,内部元素会自动排序,添加、删除、包含等操作相对低效
HashSet
数组+链表、会树化、无序
封装HashMap,无序,使用Hash算法,若Hash散列正常,可提供常数时间的添加、删除、包含操作
LinkedHashSet
链表、有序
内部构建了一个记录插入顺序的双向链表,有序的同时还能保证常数时间的添加、删除、包含等操作。这些操作效率略低于HashSet,因为要维护链表
Queue/Deque
java提供的标准队列,支持先入先出(FIFO)先入后出(FILO)
PriorityQueue
二叉树、最大值优先/最小值优先(父节点)
ArrayQueue
Map
是以键值对的形式存储和操作数据的容器类型
HashTable、HashMap、TreeMap都是Map的实现
HashTable
是早期Java提供的同步Hash表实现,不支持null键值,由于同步导致的性能开销,所以一般不推荐使用
TreeMap
一种基于红黑树且可提供顺序访问的容器,get、put、remove操作是时间复杂的(O(log(n))),put、remove操作时会涉及到排序。具体顺序可以由指定的Comparator来决定,或根据键的自然顺序来判断。
compareTo 需要与 equals 结果一致,否则会导致Key不明确(两个不同的元素被当做是同一个)
HashMap
应用最广泛的Hash表实现,行为上大致与HashTable一致,主要区别在于HashMap不是同步的,且支持null的键值。通常情况下HashMap可以达到O(1)的get和put操作
数据结构
通过数组+链表实现,hash相同的元素存放在相同链表内,新元素放在链表头部
数组寻址:
i = (n - 1) & hash,容量和hash值做与运算,会忽略hash值的高位
hash算法:
obj.hashCode >>> 16,对key的hash值做右位移
原因:
一般hash值的差异主要在高位,hashmap的数组寻址是忽略容量以上高位的
避免hash碰撞
数组大小:
门限值 = 负载因子(0.75)* 容量
门限通常整倍扩容:newThr = oldThr << 1
扩容后,复制数组
树化:
1,单个链表的大小超过阈值(TREEIFY_THRESHOLD,8)
2,容量 >= MIN_TREEIFY_CAPACITY(64)
putVal时,满足以上条件,则数据结构进行树化改造(红黑树)
HashMap循环引用问题
原因:扩容数组复制时,相同数组下标的新元素加载在老元素头部
场景:多线程并发扩容
解决:使用错误,并发场景下应使用ConcurrentHashMap
HashCode 与 equals基本约定
1,equals相等,hashCode必须相等
2,重写hashCode,必须重写equals
3,对象属性改变时,保持hashCode不变
LinkedHashMap
额外维护了双向链表,保证有序
实现LRU
实现LinkedHashMap,设置accessOrder=true,并重写removeEldestEntry,可即时释放不常被访问的元素
accessOrder:访问时将元素加载到链表头部
removeEldestEntry:移除满足条件的元素