1.Collection
Collection 存储着对象的集合
1.1 Collection常用方法
方法名 | 说明 |
---|---|
boolean add(E e) | 向集合添加元素e,若指定集合元素改变了则返回true |
boolean addAll(Collection<? extends E> c) | 把集合C中的元素全部添加到集合中,若指定集合元素改变返回true |
void clear() | 清空所有集合元素 |
boolean contains(Object o) | 判断指定集合是否包含对象o |
boolean containsAll(Collection<?> c) | 判断指定集合是否包含集合c的所有元素 |
boolean isEmpty() | 判断指定集合的元素size是否为0 |
boolean remove(Object o) | 删除集合中的元素对象o,若集合有多个o元素,则只会删除第一个元素 |
boolean removeAll(Collection<?> c) | 删除指定集合包含集合c的元素 |
boolean retainAll(Collection<?> c) | 从指定集合中保留包含集合c的元素,其他元素则删除 |
int size() | 集合的元素个数 |
T[] toArray(T[] a) | 将集合转换为T类型的数组 |
1.2 常用分类
List 接口:元素按进入先后有序保存,可重复
- ArrayList:基于动态数组实现,支持随机访问,没有同步,线程不安全
- Vector:和 ArrayList 类似基于数组,同步,线程安全
- LinkedList:基于双向链表实现,只能顺序访问,但是可以快速地在链表中间插入和删除元素。不仅如此,LinkedList 还可以用作栈、队列和双向队列。
Set 接口: 仅接收一次,不可重复,并做内部排序
- TreeSet:基于红黑树实现,支持有序性操作,例如根据一个范围查找元素的操作。但是查找效率不如 HashSet,HashSet 查找的时间复杂度为 O(1),TreeSet 则为 O(logN)。
- HashSet:基于哈希表实现,支持快速查找,但不支持有序性操作。并且失去了元素的插入顺序信息,也就是说使用 Iterator 遍历 HashSet 得到的结果是不确定的。
- LinkedHashSet:具有 HashSet 的查找效率,并且内部使用双向链表维护元素的插入顺序。
1.3 List
(1)ArrayList:底层数据结构是数组,查询快,增删慢,线程不安全,效率高,可以存储重复元素
(2)LinkedList 底层数据结构是链表,查询慢,增删快,线程不安全,效率高,可以存储重复元素
(3)Vector:底层数据结构是数组,查询快,增删慢,线程安全,效率低,可以存储重复元素
1.3.1 ArrayList
1.3.2 Vector
1.3.3 LinkedList
1.4 Set
HashSet分析
LinkedHashSet分析
TreeSet分析
2. Map
主要方法:
public int size() //当前Map的大小
public boolean isEmpty()//查询Map是否为空
public boolean containsValue(Object value)//查询Map中是否包含指定value
public boolean containsKey(Object key)
public V get(Object key)//返回指定key对应的value,如果不包含key返回null
public V put(K key, V value)
public V remove(Object key)//删除key所对应的的键值对,返回所关联的value,如果key不存在返回null
public void putAll(Map<? extends K, ? extends V> m)
public void clear()
public Set<K> keySet()//返回该Map中所有key组成的set集合
public Collection<V> values()//返回Map所有value组成的Collection
public Set entrySet()//返回Map中所包含的键值对组成的Set集合,每个集合元素都是Map.Entry对象
LinkedHashMap
- 存储结构
继承自 HashMap,因此具有和 HashMap 一样的快速查找特性。
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>
内部维护一个双向链表,用来维护插入顺序或者LRU顺序(最近最晚被使用)
/**
* The head (eldest) of the doubly linked list.
*/
transient LinkedHashMap.Entry<K,V> head;
/**
* The tail (youngest) of the doubly linked list.
*/
transient LinkedHashMap.Entry<K,V> tail;
accessOrder 决定了顺序,默认为 false,此时维护的是插入顺序。LinkedHashMap 最重要的是以下用于维护顺序的函数,它们会在 put、get 等方法中调用。
void afterNodeAccess(Node<K,V> p) { }
void afterNodeInsertion(boolean evict) { }
afterNodeAccess()
当一个节点被访问时,如果 accessOrder 为 true,则会将该节点移到链表尾部。也就是说指定为 LRU 顺序之后,在每次访问一个节点时,会将这个节点移到链表尾部,保证链表尾部是最近访问的节点,那么链表首部就是最近最久未使用的节点。
// 这个方法的作用就是将刚访问过的元素放到集合的最后一位
void afterNodeAccess(Node < K, V > e) {
LinkedHashMap.Entry < K, V > last;
if (accessOrder && (last = tail) != e) {
// 将 e 转换成 LinkedHashMap.Entry
// b 就是这个节点之前的节点
// a 就是这个节点之后的节点
LinkedHashMap.Entry < K, V > p = (LinkedHashMap.Entry < K, V > ) e, b = p.before, a = p.after;
// 将这个节点之后的节点置为 null
p.after = null;
// b 为 null,则代表这个节点是第一个节点,将它后面的节点置为第一个节点
if (b == null) head = a;
// 如果不是,则将 a 上前移动一位
else b.after = a;
// 如果 a 不为 null,则将 a 节点的元素变为 b
if (a != null) a.before = b;
else last = b;
if (last == null) head = p;
else {
p.before = last;
last.after = p;
}
tail = p;
++modCount;
}
}
afterNodeInsertion()
在 put 等操作之后执行,当 removeEldestEntry() 方法返回 true 时会移除最晚的节点,也就是链表首部节点 first。
evict 只有在构建 Map 的时候才为 false,在这里为 true。
void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry<K,V> first;
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
removeNode(hash(key), key, null, false, true);
}
}
removeEldestEntry() 默认为 false,如果需要让它为 true,需要继承 LinkedHashMap 并且覆盖这个方法的实现,这在实现 LRU 的缓存中特别有用,通过移除最近最久未使用的节点,从而保证缓存空间足够,并且缓存的数据都是热点数据。
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}
LRU缓存
以下是使用 LinkedHashMap 实现的一个 LRU 缓存:
设定最大缓存空间 MAX_ENTRIES 为 3;
使用 LinkedHashMap 的构造函数将 accessOrder 设置为 true,开启 LRU 顺序;
覆盖 removeEldestEntry() 方法实现,在节点多于 MAX_ENTRIES 就会将最近最久未使用的数据移除。
class LRUCache<K, V> extends LinkedHashMap<K, V> {
private static final int MAX_ENTRIES = 3;
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > MAX_ENTRIES;
}
LRUCache() {
super(MAX_ENTRIES, 0.75f, true);
}
}
public static void main(String[] args) {
LRUCache<Integer, String> cache = new LRUCache<>();
cache.put(1, "a");
cache.put(2, "b");
cache.put(3, "c");
cache.get(1);
cache.put(4, "d");
System.out.println(cache.keySet());
}
借鉴链接:LruCache详解
Question:
Q: Java集合框架中有哪些类?都有什么特点
Q:集合、数组、泛型的关系,并比较
Q: ArrayList和Linklist的区别?
Q: ArrayList和Vector的区别?
Q: HashSet和TreeSet的区别?
Q: HashMap和Hashtable的区别?
Q: HashMap在put、 get元素的过程?体现了什么数据结构?
Q:如何解决Hash冲突?
Q:如何保证HashMap线程安全?什么原理?
Q: HashMap是有序的吗?如何实现有序?
Q: HashMap是如何扩容的?如何避免扩容?
Q: hashcode的作用, 与equal有什么区别?