类图关系
Collection实现了Iterable接口,通过Iterable接口定义的Interator < T> iterator()方法来获得Iterator对象。
Iterator<T> iter=list.iterator();
if(iter.hasNext()){
T temp=iter.next();
}//next()方法既返回一个当前Iterator对象,又把游标向下移动一个单位。
1. 列表 List
实现List接口的集合类,集合中的元素插入有序,可以重复。
- ArrayList
数组实现,线程不安全,初始大小为10(可传参),扩容×1.5(不可变),查找快、增删慢
扩容、remove、add操作都是拷贝;clear操作是数组元素依次置为null。
Object[] elementData;//核心存储结构
private static final int DEFAULT_CAPACITY = 10;
注意:方法前没有synchronized关键字;
可能出现的异常:java.util.ConcurrentModificationException
解决ArrayList线程不安全方法:
(1)new Vector<>();
(2)Collections.synchronizedList(new ArrayList<>());
(3)new CopyOnWriteArrayList<>(); (√)
写时复制,读写分离的思想:
- LinkedList
双向链表实现,线程不安全,查找慢、增删快
Node <E> first/last=new Node<E>(null,null,null);//核心存储结构
- Vector
数组实现,线程安全,初始大小10(可传参),扩容×2(可传参),查找快、增删慢
protected Object[] elementData;//存储结构:数组,同ArrayList
vector初始大小默认为10,capacityIncrement值默认为0,即扩容×2。
synchronized关键字实现线程同步:
2. 集合 Set
实现Set接口的集合类,集合中的元素不可重复。
- HashSet
线程不安全,无序,可以容纳null元素,内部是HashMap实现的,数据结构是hash表。
HashMap<E,Object> map;//核心存储结构
private static final Object PRESENT=new Object();//value均设为此值
可能出现的异常:java.util.ConcurrentModificationException
解决HashSet线程不安全方法:
(1)Set set=Collections.synchronizedSet(new HashSet<>());
(2)Set set=new CopyOnWriteArraySet<>();
//底层是CopyOnWriteArrayList
- LinkedHashSet
线程不安全,插入有序,可以容纳null元素,内部是LinkedHashMap实现的,数据结构是hash表+双向链表维护插入顺序
- TreeSet
线程不安全,元素有序,不能容纳null元素,内部是TreeMap实现的,数据结构是二叉树
-HashSet和LinkedHashSet判定元素重复的原则:
判定两个元素的hashCode返回值是否相同,不同则返回false;若两者hashCode相同,则判定equals方法,不同返回false,相同返回true。hashcode->equals()
-Treeset判定元素重复的原则:
元素需要继承Comparable接口,比较两个元素的compareTo方法
TreeMap <E,Object>;//核心存储结构
3. 映射 Map
- HashMap
K-V对,K和V都允许为null,线程不安全,无序,数据结构是hash表。
Java(HashMap)中规定,两个内容相同(equals()为true)的对象必须具有相等的hashCode,否则整个存储过程就发生了悖论。
transient Node<K,V>[] table;//存储结构,Node对象数组
static class Node<K,V> implements Map.Entry<K,V>//Node内部类实现了Entry接口
HashMap的位桶数组,初始大小为16(可以在取模和扩容时作优化,可以修改),其中元素达到0.75(负载因子,可以修改)×length,就重新调整数组大小变为原来的2倍。扩容很耗时,本质是定义新的更大的数组,并将旧数组内容挨个拷贝到新数组中。HashMap最大容量为1<<30即2^30(int4个字节,含符号位)。
JDK7中,HashMap底层是数组+链表实现;JDK8中,HashMap在存储一个元素时,当对应链表长度大于8时,链表就转换为红黑树,这样又大大提高了查找的效率,在长度小于6时,转回链表。
JDK7中扩容时由于线程不同步数组的链表可能出现环,就可能出现死锁问题;JDK8中改进扩容机制,使用尾插法,使扩容后的hashMap保持顺序,出现死锁的概率大大降低。
位桶数组大小是2的次幂,JDK1.8扩容时只需要判断Hash值的新增参与运算的位是0还是1就直接迅速计算出了扩容后的储存方式,而不需要重新进行异或。
//JDK1.8
putVal方法{
如果table为空或长度为0,则进行resize()扩容;
计算要插入元素的hash值,即对应位桶数组下标((n-1)&hash),如果对应位置为空{
即此时无hash冲突,直接放入,作为对应位置第一个Node对象;
}
发生hash冲突{
如果对应位置第一个Node对象key值与要put对象的key值相等,直接覆盖;
否则判断是否为TreeNode数据结构,是则直接调用putTreeVal方法;
不是TreeNode结构,即为链表结构{
遍历链表,如果遍历到key相等的Node对象,则覆盖;
如果遍历到尾部也没有key相等的Node对象{
则创建一个新的Node对象,插入尾部;
如果binCount链表长度大于8,则转为红黑树结构存储;
}
}
}
如果位桶数组长度超过阈值,就resize()扩容;
}
//JDK1.8
getNode方法{
如果位桶数组table不为空&&长度不为0&&要找的key的hash值在table中对应位置第一个元素不为null{
如果对应位置第一个元素就是要找的值,直接返回;
否则,如果对应位置第一个元素的next不为null{
如果是红黑树数据结构,调用getTreeNode直接返回;
否则为链表结构,遍历链表,找到则返回;
}
}
执行到此处也说明没找到,返回null;
}
HashMap可能出现的异常:java.util.ConcurrentModificationException
解决HashMap线程不安全方法:
(1)Collections.synchronizedMap(new HashMap<>());
(2)new ConcurrentHashMap<>();
- LinkedHashMap
基于双向链表维持插入顺序的HashMap
- TreeMap
V允许为null,基于红黑树的Map,可以根据key的自然排序或者compareTo方法进行key元素的排序输出。需要实现Comparable接口。
TreeMap和HashMap实现了同样的接口Map,因此用法对于调用者来说没有区别。HashMap效率高于TreeMap;在需要排序的Map时才选用TreeMap,对应类要实现Comparable接口。
- Hashtable
K-V对,K和V都不允许为null,线程安全,无序。初始化大小为11(相对来说素数导致的冲突概率小于合数),扩容*2+1。
HashTable使用synchronized锁住整张Hash表实现线程安全。
- Properties
继承于Hashtable,可以将K-V对保存在文件中
4. 工具类
- Arrays
作用对象是数组,常用方法:
sort(排序)
binarySearch(二分查找)
copyOf(批量复制)
fill(批量赋值)
equals(等价性比较)
2. Collections
作用对象是Collection及其子类,常用方法:
sort
binarySearch
fill
max、min
reverse(反序)
补充
对象比较(如使用Collections.sort(Target))时:
- 需要对象实现Comparable接口并实现compareTo方法。
- 或者新建一个实现Comparator接口的类对象并实现compare方法作为参数传递(适用于对象类不可更改的情况)。