Java集合
List, Set, Queue, Map
List:有序的、可重复的
Arraylist: Object[] 数组
Vector:Object[] 数组
LinkedList: 双向链表
Set:无序的、不可重复的
HashSet: (无序,唯一) 基于 HashMap 实现的,底层采用 HashMap 来保存元素
LinkedHashSet: 内部是通过 LinkedHashMap 来实现的
TreeSet: (有序,唯一)红黑树(自平衡的排序二叉树)
Queue:有序的、可重复的
PriorityQueue: Object[] 数组来实现二叉堆
ArrayQueue: Object[] 数组 + 双指针
Map:键值对,key 是无序的、不可重复的,value 是无序的、可重复的
HashMap:JDK1.8 之前 HashMap 由数组+链表组成的,JDK1.8 以后当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间
LinkedHashMap:基于拉链式散列结构即由数组和链表或红黑树组成,并且增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序
Hashtable:数组+链表组成
TreeMap: 红黑树
ArrayList的扩容机制
ArrayList 的底层是数组队列(transient Object[] elementData),相当于动态数组。
默认初始化大小为DEFAULT_CAPACITY=10;
扩容大小为:新长度=旧长度+旧长度/2
使用Arrays.copyOf()调用System.arraycopy进行复制。
System.arraycopy(original, 0, copy, 0,Math.min(original.length, newLength));
System.arraycopy(源数组,源数组中的起始位置,目标数组,目标数组中的起始位置,要复制的数组元素的数量)
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
// 元素长度加1,判断是否需要扩容。
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// 如果是空数组,直接初始化为10,不是的话返回容器大小长度
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 判断是否需要扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//进行扩容
private void grow(int minCapacity) {
//旧长度
int oldCapacity = elementData.length;
// 新长度=旧长度+旧长度/2
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
Arraylist 与 LinkedList 区别
线程安全
数据结构
插入和删除
快速随机访问
内存空间占用
comparable 和 Comparator 的区别
comparable 接口 compareTo(Object obj)=>类实现方法,在对比的时候进行调用
comparator接口 compare(Object obj1, Object obj2) => 直接在对比的时候进行匿名函数使用
Queue 与 Deque 的区别
Queue 是单端队列,只能从一端插入元素,另一端删除元素(先进先出(FIFO)
Deque 是双端队列,在队列的两端均可以插入或删除元素
Queue相关方法:
add(E e)/offer(E e)
remove()/poll()
element()/peek()
Deque 相关方法:
addFirst(E e)/offerFirst(E e)
addLast(E e)/offerLast(E e)
removeFirst()/pollFirst()
removeLast()/pollLast()
getFirst()/peekFirst()
getLast()/peekLast()
ArrayDeque 与 LinkedList 的区别
ArrayDeque 是基于可变长的数组和双指针来实现,而 LinkedList 则通过链表来实现。
ArrayDeque 不支持存储 NULL 数据,但 LinkedList 支持
HashMap 和 Hashtable 的区别
线程安全
效率
HashMap 可以存储 null 的 key 和 value,但 null 作为键只能有一个,null 作为值可以有多个;Hashtable 不允许有 null 键和 null 值,否则会抛出 NullPointerException。
初始容量大小和每次扩充容量大小的不同(Hashtable 默认的初始大小为 11,之后每次扩充,容量变为原来的 2n+1。HashMap 默认的初始化大小为 16。之后每次扩充,容量变为原来的 2 倍)
底层数据结构:HashMap:拉链式散列结构 ;Hashtable:数组
HashMap 和 TreeMap 区别
相比于HashMap来说 TreeMap 主要多了对集合中的元素根据键排序的能力以及对集合内元素的搜索的能力
HashMap 的底层实现
JDK1.8 之前:链表散列。
添加元素方式:HashMap 通过 key 的 hashCode 经过扰动函数处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置(这里的 n 指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。
JDK1.8 之前:链表散列红黑树。当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。
HashMap 的长度为什么是 2 的幂次方
尽量把数据分配均匀,减少hash碰撞+公式。hash%length==hash&(length-1)。取余(%)操作中如果除数是 2 的幂次则等价于与其除数减一的与(&)操作
HashMap常见的遍历方式
EntrySet=》entry.getValue();
entry.getKey() ,KeySet=》map.get(key);
迭代器(Iterator)方式遍历=》map.entrySet().iterator();/ map.keySet().iterator();
For Each 方式遍历
Lambda 表达式遍历
Streams API 遍历
最佳方法:
Iterator<Map.Entry<Integer, String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Integer, String> entry = iterator.next();
Integer k = entry.getKey();
String v = entry.getValue();
}
ConcurrentHashMap 和 Hashtable 的区别
数据结构
JDK1.7 的 ConcurrentHashMap 底层采用 分段的数组+链表
JDK1.8 的 ConcurrentHashMap 底层采用数组+链表/红黑二叉树
线程安全
JDK1.7 ConcurrentHashMap(分段锁)切割成Segment;一个 Segment 包含一个 HashEntry 数组
JDK1.8 直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作。
PriorityQueue
PriorityQueue 利用了二叉堆的数据结构来实现的,底层使用可变长的数组来存储数据
PriorityQueue 通过堆元素的上浮和下沉,实现了在 O(logn) 的时间复杂度内插入元素和删除堆顶元素。
PriorityQueue 是非线程安全的,且不支持存储 NULL 和 non-comparable 的对象。
PriorityQueue 默认是小顶堆,但可以接收一个 Comparator 作为构造参数,从而来自定义元素优先级的先后。
Collections 工具类常用方法
排序;查找,替换操作;同步控制
常见集合操作
集合转 Map:bookList.stream().collect(Collectors.toMap(Person::getName, Person::getPhoneNumber));
在使用 java.util.stream.Collectors 类的 toMap() 方法转为 Map 集合时,一定要注意当 value 为 null 时会抛 NPE 异常。
集合转数组:要指定类型
s=list.toArray(new String[0]);
数组转集合
Arrays.asList() 方法返回的并不是 java.util.ArrayList ,而是 java.util.Arrays 的一个内部类,这个内部类并没有实现集合的修改方法或者说并没有重写这些方法。
1.List list = new ArrayList<>(Arrays.asList(“a”, “b”, “c”))
2.List myList = Arrays.stream(myArray2).boxed().collect(Collectors.toList());
3.List list = List.of(array);
不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator 方式,如果并发操作,需要对 Iterator 对象加锁。
Java8 开始,可以使用 Collection#removeIf()方法删除满足特定条件的元素
集合去重
List data
return new HashSet<>(data);