一、ArrayList
- 动态扩充数组
- 实现原理:采用动态对象数组,默认构造数组创建了一个空数组(默认大小为0)
- 第一次添加元素,扩容为10,之后的扩充算法:新容量 = 老容量 + 老容量的一半 (1.5倍老容量)
- 动态数组不适合删除、插入(移位)
- 最好存储相同类型的元素,以免类型转换麻烦
- 动态扩充次数过多或影响性能,所以建议在创建ArrayList时给定初始容量
- 线程不安全,适合单线程
- 可以有空元素
二、Vector
- 实现原理:采用动态对象数组,默认构造数组创建了一个空数组(默认大小为10)
- 扩容算法:如果没有自己设置增量(为0),则 新容量 = 老容量 + 老容量(2倍老容量);
增量大于0时,新容量 = 老容量 + 增量 - 动态数组不适合删除、插入(移位)
- 动态扩充次数过多或影响性能,所以建议在创建Vector时给定初始容量
- 加了synchronize 线程安全,可以在多线程下使用,但是效率低
- 也可以放null元素
三、LinkList
- 实现原理:采用双向链表结构
- 适合插入、删除操作,性能高
四、Connection接口
Connection接口:用于存储单个对象的集合
五、List接口
1、有序 2、允许多个null元素 3、具体实现类: ArrayList、Vector、LinkList
实际开发中如何选择?
- 安全性?
Vector ps:也可以用ArrayList的安全工具类CopyOnWriteArrayList - 频繁插入、删除?
LinkList - 存储后遍历?
ArrayList
六、HashMap
- 实现原理:哈希表(数组+链表+红黑树)
- 把对象存储到哈希表中:如何存储(put)?
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
key和value是存储在HashMap里面一个静态类Node里面的
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
}
过程:
- 把key对象通过hash()方法计算哈希值,
hash(key) = (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); 计算hashCode值再与右移16位后的值异或 ps:基本上还是原来的自己,因为右移16位极小 - 然后用这个哈希值对数组长度(默认16)取余数 来决定该key对象在数组中的存储位置
- 当这个位置已经有多个对象是,以链表结构存储,(jdk1.8后)再当链表长度大于8时,链表将转化为红黑树
目的:取值更快(通过计算key来确定存放的地方); 存储数据量越大,性能表现越明显
-
数组扩充原理: 数组容量超过75%后,数组需要扩充:resize() 当前数组容量左移一位<<2 (也就是原来的两倍) 注意:然后哈希表要重新散列(重新计算每个对象的存储位置) 缺点:扩充次数过多会影响性能(每次扩容后全部对象都要重新排位) 在开发中要尽量减少扩充次数 -
线程不安全
DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 默认初始容量大小MAXIMUM_CAPACITY = 1 << 30; 最大容量值DEFAULT_LOAD_FACTOR = 0.75f; 加载因子(负载因子) :数组当中75%的空间如果都被存了值,那这个数组空间就达到上限了,需要重新散列(创建) 更高的值会降低空间开销,但是增加查找成本(get、put)TREEIFY_THRESHOLD = 8; 当桶内链表节点数量大于8的时候,链表转化为红黑树UNTREEIFY_THRESHOLD = 6; 当桶内链表节点数量小于6的时候,采用链表存储。 (红黑树转化为链表)MIN_TREEIFY_CAPACITY = 64; 桶中结构转化为红黑树对应的数组的最小大小,如果当前容量小于它,就不会将链表转化为红黑树,而是用resize()代替 桶中bin 被树化时,最小的hash表容量,默认为 64 。当散列表容量小于该阈值,即使桶中bin的数量超过了 treeify_threshold ,也不会进行树化,只会进行扩容操作。 为了避免进行扩容、树形化选择的冲突,它至少是 treeify_threshold 的4倍。 ps: 也就是说 链表想要树化,哈希表中的容量要大于它(64),否则,桶内元素太多时,会直接扩容,而不是树形化
|