集合主要分为4类:
List,Set,Queue,Map
ArrayList实现原理(1.5倍扩容)
ArrayList底层是一个动态数组,默认初始长度为10,也可以在构造函数里面声明,如果数组被填满,则会创建一个容量为当前的1.5(loadFactor)倍的数组,并使用Arrays.copyOf方法把该数组的内容复制到这个数组里面去,支持随机访问,O(1),但是线程不安全。可以考虑Vector或juc包下的集合。
ArrayList和LinkedList区别
- ArrayList是采用数组存储的,占用连续的内存空间,可能会产生碎片,占用空间比较小,而LinkedList采用双向链表存储,除了存储数据外还要存储指向前一个数据和后一个数据的指针,占用空间较大
- ArrayList采用索引寻找数据,所以查找的复杂度为O(1),而LinkedList需要从头或尾遍历才能得到数据,所以为O(n);
- ArrayList插入,删除的复杂度为O(n),而LinkedList为O(1)
HashMap的实现原理(2倍扩容)
HashMap底层是使用一个长度为16的数组(哈希表)实现,每个数组元素是一个链表或者是一个红黑树的头结点,称为桶;当调用put方法添加数据时,会根据key的hash值取模运算,得到存放位置,当hash冲突的时候会采用链式存储,将冲突的元素用尾插法存到一个链表里面;另外为了避免链表过长导致查询效率下降,当链表长度大于8并且数组长度大于64的时候,链表会转为红黑树,从而减少链表查询的时间复杂度。负载因子为0.75,当容量达到总容量*0.75时会扩容为原来的两倍,当然因为容量改变了,也会重新哈希,即重新计算各个key的位置。
注意:HashMap的不会根据键值的大小来决定位置,只有TreeMap可以,String的hashCode是以每个字符乘以一个常数累加得到的。
HashTable
HashTable初始容量是11,之后每次扩容是2N+1,线程安全的使用synchronized锁
元素为空问题
- ArrayList和LinkedList元素都可以为空,并且可以有多个;
- HashMap的键值都可以为null,但是键只能有一个为null,否则会覆盖键,值可以有多个;
- HashTable的键和值都不能为空,否则抛出空指针异常
- ConcurrentHashMap键值都不能为null,因为是线程安全的,所以当一个线程调用containsKey(key)的时候,如果这个key本身就不存在,返回false,如果此时另一个线程调用put(key,null),然后线程又会返回true,取决于两个线程的先后顺序,故造成了线程不安全,而ConcurrentHashMap本身就是一个线程安全的,所以都不能为null
集合框架的CRUD
ArrayList和Vector:ArrayList和Vector都是动态数组,区别是Vector线程安全而ArrayList不安全,故性能反之。
对于栈和队列:
栈和队列经常使用ArrayDeque和LinkedList(实现Deque(继承Queue))来使用,当然ArrayDeque也可以当动态数组来使用;
ArrayDeque和LinkedList有栈 | 队列 | 动态数组所有的增加删除方法
栈的增加/删除方法:push | pop,peek访问栈顶元素
队列的增加/删除方法:offer | poll
动态数组的增加/删除/修改/查询方法:add | remove | set(index) | get(index)
集合的增加/删除:add | remove 【因为集合是无序的,所以没有查询方法】
Hash的增加/删除/查询方法:put | remove(key) | get(key)
堆:Queue heap = new PriorityQueue(),默认小顶堆;
通过向构造方法里面传比较器可实现大顶堆;Queue heap = new PriorityQueue((a,b) -> b - a);
hashCode()-equals()-哈希冲突问题
- 一定要重写equals的同时重写hashCode,否则集合类使用的时候会造成相同的两个对象存储在不同位置,因为默认使用JVM的随机数充当hashCode,集合类先使用hashCode判断,再使用equals判断,方法重写可以使用lombok提供的@EqualsAndHashCode
- 若hashCode不同,则肯定不是同一个键,也就不会发送哈希冲突
- 当哈希冲突时,即hashCode相同,这时会通过equals比较,若返回true,则说明是同一个对象,会将新的value值覆盖原来的value值;否则会放到该桶下面的链表中。
使用LinkedHashMap实现LRU
class LRUCache extends LinkedHashMap<Integer, Integer>{
private int capacity;
public LRUCache(int capacity){
super(capacity,0.75F,true);
this.capacity = capacity;
}
public int get(int key){
return super.getOrDefault(key,-1);
}
public void put(int key, int value){
super.put(key,value);
}
@Override
protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
return super.size() > capacity;
}
}
重写的方法是protected方法,必须继承父类才能用
本文详细介绍了Java中的各种集合类,如ArrayList、LinkedList、HashMap、HashTable和ConcurrentHashMap,对比了它们的实现原理、性能优劣以及CRUD操作。还讨论了equals和hashCode的重要性,以及如何使用LinkedHashMap实现LRU缓存策略。

被折叠的 条评论
为什么被折叠?



