集合
集合
单列集合
List
- List 集合类中元素有序(即添加顺序和取出顺序一致)、且可重复
- List 集合中的每个元素都有其对应的顺序索引,即支持索引
Vector
创建源码分析
//底层是一个对象数组
protected Object[] elementData;
//不同于ArrayList Vector方法都是Synchronized 代表是线程同步的
public synchronized boolean isEmpty() {
return elementCount == 0;
}
//有参创建流程
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
//无参创建流程
public Vector() {
//初始容量为10
this(10);
}
扩容机制是
//当没有指定容量是 初始容量为10+需要增加的容量>0(false) 选择初始化容量 所以初始容量+初始容量
//当指定容量时 初始化容量为指定容量+指定容量是否>0(ture) 选择指定容量 所以指定容量+指定容量
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
Array List
- Array List中维护了一个 object:类型的数组 element Data
- transient object[] element Data;transient表示瞬间短暂的表示该属性不会被序
- 当创建 Array List对象时,如果使用的是无参构造器,则初始 element Data容量为0,第一次添加,则扩容 element Data为10,如需要再次扩容,则扩容 element Data为1.5倍
- 如果使用的是指定大小的构造器,则初始 element Data容量为指定大小,如果需要扩容则直接扩容 element Data为1.5倍
创建源码分析
//无参创建流程
ArrayList list =new ArrayList();
public ArrayList() {
//创建一个elementData空数组
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//elementData空数组 transient:将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会被序列化
transient Object[] elementData; // non-private to simplify nested class access
//有参构造器创建流程
ArrayList list = new ArrayList(16);
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
//创建一个指定大小的elementData数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
//0的话还是空数组
this.elementData = EMPTY_ELEMENTDATA;
} else {
//抛出异常初始容量不对
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
添加流程(add)

https://images.cnblogs.com/cnblogs_com/blogs/697861/galleries/2036654/o_210926081035LinkedList.png
//e 添加的数据
public boolean add(E e) {
//这里先确定是否需要扩容
ensureCapacityInternal(minCapacity:size + 1); // Increments modCount!!
//size初始化为0
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
//calculateCapacity判断初始容量大小的封装方法 ensureExplicitCapacity判断是否继续扩容
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
初始化容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//判断是否为空数组
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//private static final int DEFAULT_CAPACITY = 10;
//判断默认大小和最小容量哪个大 并且返回
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
//protected transient int modCount = 0;
//为了记录当前集合被修改的次数,防止多线程操作出现的异常
modCount++;
// overflow-conscious code
//这里最小容量-数组实际大小>0就说明 需要扩容
if (minCapacity - elementData.length > 0)
//扩容方法 这里就能看到扩容多少了
grow(minCapacity);
}
扩容
private void grow(int minCapacity) {
// overflow-conscious code
//实际容量
int oldCapacity = elementData.length;
//新的容量这里能看出是大概为1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//这里因为刚进入方法oldCapacity为0 newCapacity也就为0
//这意思是新的容量还没有最小容量大
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);
}
huge Capacity
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
//内存溢出
throw new OutOfMemoryError();
//Integer.MAX_VALUE
//private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; ???对象头的大小不能超过8个字节
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
Linked List
- Linked List底层实现了双向链表和双端队列特点
- 可以添加任意元素(元素可以重复),包括null,线程不安全,没有实现同步
Linked List底层的双向链表
//首节点
transient Node<E> first;
//尾节点
transient Node<E> last;
//node节点
private static class Node<E> {
E item;
//指向前一个节点
Node<E> next;
//指向后一个节点
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
模拟简单的双向链表
public static void main(String[] args) {
Node J = new Node("J");
Node Q = new Node("Q");
Node K = new Node("老韩");
//连接三个结点,形成双向链表
//J -> Q -> K
J.next = Q;
Q.next = K;
//K -> Q -> J
K.pre = Q;
Q.pre = J;
//让first引用指向J,就是双向链表的头结点
Node first = J;
//让last引用指向K,就是双向链表的尾结点
Node last = K;
//从头到尾进行遍历
System.out.println("===从头到尾进行遍历===");
while (true) {
if(first == null) {
break;
}
//输出first 信息
System.out.println(first);
first = first.next;
}
//从尾到头的遍历
System.out.println("====从尾到头的遍历====");
while (true) {
if(last == null) {
break;
}
//输出last 信息
System.out.println(last);
last = last.pre;
}
//1. 先创建一个 Node 结点,name 就是 smith
Node smith = new Node("smith");
//下面就把 smith 加入到双向链表了
smith.next = K;
smith.pre = Q;
K.pre = smith;
Q.next = smith;
//让first引用指向J,就是双向链表的头结点
first = J;
System.out.println("===从头到尾进行遍历===");
while (true) {
if(first == null) {
break;
}
//输出first 信息
System.out.println(first);
first = first.next;
}
//让last 重新指向最后一个结点
last = K;
//演示,从尾到头的遍历
System.out.println("====从尾到头的遍历====");
while (true) {
if(last == null) {
break;
}
//输出last 信息
System.out.println(last);
last = last.pre;
}
}
private static class Node {
/**
*节点的值
*/
public Object item;
/**
*当前节点的后一个节点的引用
*/
public Node next;
/**
*当前节点的前一个节点的引用
*/
public Node pre;
public Node(Object name) {
this.item = name;
}
@Override
public String toString() {
return "Node name=" + item;
}
}

创建源码分析
LinkedList linkedList = new LinkedList();
linkedList.add(1);
linkedList.add(2);
linkedList.add(3);
第一步
//无参创建流程
public LinkedList() {
}
//数量
transient int size = 0;
public boolean add(E e) {
linkLast(e);
//添加成功
return true;
}
void linkLast(E e) {
//e代表添加的数据 e=linkedList.add(1),此时last为null
final Node<E> l = last;
//创建一个新节点
final Node<E> newNode = new Node<>(l, e, null);
//last指向新节点new Node<>(l, e, null)
last = newNode;
if (l == null)
//first也指向新节点new Node<>(l, e, null)
first = newNode;
else
l.next = newNode;
//添加一个元素
size++;
//记录修改一个元素
modCount++;
}
//第二次添加
void linkLast(E e) {
//此时e代表第二次添加的数据 e=linkedList.add(2), 此时last不为null last指向new Node<>(l, e, null) 这个节点
final Node<E> l = last;
//创建一个新节点 此时l=new Node<>(l, e, null) 这个节点
final Node<E> newNode = new Node<>(l, e, null);
//此时last指向新节点new Node<>(l, 2, null)
last = newNode;
if (l == null)
//first也指向新节点new Node<>(l, e, null)
first = newNode;
else
l.next = newNode;
//添加一个元素
size++;
//记录修改一个元素
modCount++;
}

删除源码分析
linkedList.remove();
public E remove() {
return removeFirst();
}
public E removeFirst() {
//让f指向第一个节点 Node<>(null, 1,下一个元素为2的节点 )
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
private E unlinkFirst(Node<E> f) {
// assert f == first && f != null;
//拿出第一个节点的元素
final E element = f.item;
//获取到下一个节点
final Node<E> next = f.next;
//此时就把第一个节点里面的内容1变成null f = Node<>(null,null,下一个元素为2的节点 )
f.item = null;
//此时就把第一个节点里面的下一个元素为2的节点变成null f = Node<>(null,null,null )
//这时候这个就删除了
f.next = null; // help GC Gc就回收了
//这里又把第一个节点指向第二个节点了 个人理解是前面那个垃圾节点已经被回收了 下一个元素为2的节点就变成了第一个节点
first = next;
//这里判断 此时next应该是 Node<>(上一个节点,2,下一个元素为3的节点 )
//上一个节点===此时节点还没变化是Node<>(null, 1,下一个元素为2的节点 )
if (next == null)
last = null;
else
//这个它把next变成Node<>(null,2,下一个元素为3的节点 )
next.prev = null;
//LinkedList里面的元素减少一个
size--;
//LinkedList修改的元素加一个
modCount++;
return element;
}
修改源码分析
linkedList.set(1, 999);
public E set(int index, E element) {
checkElementIndex(index);
//获取到对应索引的节点
Node<E> x = node(index);
//获取到对应索引的节点里面的元素
E oldVal = x.item;
//把修改的元素赋给要修改的
x.item = element;
return oldVal;
}
//检查元素索引
private void checkElementIndex(int index) {
//判断没有这个元素索引
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
Set
- 不能存放重复的元素, 可以添加一个null
- 接口对象存放数据是无序(即添加的顺序和取出的顺序不一致)
- 注意:取出的顺序的顺序虽然不是添加的顺序,但是他的取出顺序固定
- 遍历:迭代器 增强for 接口对象无法使用索引来获取
Hash Set
Hash Set底层就是Hash Map
public HashSet() {
map = new HashMap<>();
}
在执行add方法后,会返回一个boolean值 remove 指定删除哪个对象
Hash Set 可以存放null ,但是只能有一个null,即元素不能重复
添加元素的底层实现
添加一个元素时,先得到Hash值这个Hash值会转变成索引值,找到存储数据表(节点数组数组里面存放的就是链表),看这个索引位置是否已经存放元素,如果没有直接插入,如果有调用equals方法比较,如果相同则放弃添加,如果不相同,则添加到最后;在JDK8中如果一条链表的元素个数到达8,并且table的大小>=64就会进行树化(红黑树)。
添加源码分析
public boolean add(E e) {
//e是添加的元素 PRESENT就是Obkect对象 private static final Object PRESENT = new Object();
return map.put(e, PRESENT)==null;
}
//key =e value =private static final Object PRESENT = new Object();
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
//计算hashCode值
static final int hash(Object key) {
int h;
//hashcode和无符号右移16位的hashcode后得到的值做异或运算
//(h = key.hashCode()) ^ (h >>> 16)得到hash值 为了避免重复
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
/**
* Implements Map.put and related methods
*
* @param hash hash过后的key
* @param key 就是e
* @param value private static final Object PRESENT = new Object();
* @param onlyIfAbsent 如果为true,则不更改现有值
* @param evict if false, 该表处于创建模式。
* @return 以前的值,如果没有,则为null
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
//辅助变量
Node<K,V>[] tab; Node<K,V> p; int n, i;
//table就是HashMap的Node<K,V>[]节点数组 if语句表示当前table是null或者大小=0
if ((tab = table) == null || (n = tab.length) == 0)
//第一次扩容,到16个空间
n = (tab = resize()).length;
//根据key,得到hash 去计算该key应该存放到table表的哪个索引位置并把这个位置的对象,赋给 p
//判断p 是否为null 如果p 为null, 表示还没有存放元素, 就创建一个Node (key=添加的元素,value=PRESENT)
//就放在该位置 tab[i] = newNode(hash, key, value, null)
//当key相同时此时这里不会为空
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
//相同的key会走这里
Node<K,V> e; K k;
//hash值是否相同
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
//p 是不是一颗红黑树,
else if (p instanceof TreeNode)
//是一颗红黑树,就调用 putTreeVal , 来进行添加
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
// table 对应索引位置,已经是一个链表, 就使用 for 循环比较
for (int binCount = 0; ; ++binCount) {
//依次和该链表的每一个元素比较后,都不相同, 则加入到该链表的最后
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
//注意在把元素添加到链表后,立即判断 该链表是否已经达到 8 个结点
//就调用 treeifyBin() 对当前这个链表进行树化(转成红黑树)
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
//依次和该链表的每一个元素比较过程中,如果有相同情况,就直接 break
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
//HashMap留给子类的方法 对于HashMap为空 比如可以让LinkedHashSet为有序
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
final Node<K,V>[] resize() {
//让oldTab指向Table
Node<K,V>[] oldTab = table;
//此时oldCap=0
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else {
// zero initial threshold signifies using defaults
//初始容量 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
newCap = DEFAULT_INITIAL_CAPACITY;
//static final float DEFAULT_LOAD_FACTOR = 0.75f;
//临界值: 0.75*16
//为啥是加载因子是0.75? 0.75正好是3/4,而capacity又是2的幂。所以,两个数的乘积都是整数
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
//此时node节点数组里面有16个空间
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
final void treeifyBin(Node<K,V>[] tab, int hash) {
int n, index; Node<K,V> e;
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
//如果成立 table 扩容.
resize();
//进行转成红黑树
else if ((e = tab[index = (n - 1) & hash]) != null) {
TreeNode<K,V> hd = null, tl = null;
do {
TreeNode<K,V> p = replacementTreeNode(e, null);
if (tl == null)
hd = p;
else {
p.prev = tl;
tl.next = p;
}
tl = p;
} while ((e = e.next) != null);
if ((tab[index] = hd) != null)
hd.treeify(tab);
}
}
Linked Hash Set
- Linked Hash Set是Hash Set的子类
- Linked Hash Set底层是一个Linked Hash Map,底层维护了一个数组+双向链表
- Linked Hash Set根据元素的hashcode值来决定元素的存储位置,同时使用链表维护元素的次序,使得元素看起来是以插入顺序保存的
- Linked Hash Set不允许添重复元素
- Linked Hash Set 加入顺序和取出元素/数据的顺序一致
- Linked Hash Set 底层维护的是一个Linked Hash Map(是Hash Map的子类)
- Linked Hash Set 底层结构 (数组table+双向链表)
- 添加第一次时,直接将 数组table 扩容到 16 ,存放的结点类型是 Linked Hash Map$Entry
- 数组是 Hash Map$Node[] 存放的元素/数据是 Linked Hash Map$Entry类型
//继承关系是在内部类完成.
static class Entry<K,V> extends HashMap.Node<K,V> {
//定义了before, after属性
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
Tree Set
- Tree Set底层是二叉树,可以对对象元素进行排序,但是自定义类需要实现comparable接口,重写comparaTo() 方法。
- Tree Set 可以保存对象元素的唯一性(并不是一定保证唯一性,需要根据重写的compaaTo方法来确定)
//构造器把传入的比较器对象,赋给了 TreeSet的底层的 TreeMap的属性this.comparator
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
在调用 treeSet.add(), 在底层会执行到
////cpr 就是我们的匿名内部类(对象)
if (cpr != null) {
do {
parent = t;
//动态绑定到我们的匿名内部类(对象)compare
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
////如果相等,即返回0,这个Key就没有加入
return t.setValue(value);
} while (t != null);
}
双列集合
Map
- Map 与 Collection 并列存在。用于保存具有映射关系的数据:Key-Value(双列元素)
- Map 中的 key 和value 可以是任何引用类型的数据,会封装到 Hash Map$Node 对象中
- Map 中的 key 不允许重复,原因和 Hash Set 一样,调用Hash算法。
- Map 中的 value 可以重复Map 的 key 可以为 null, value 也可以为 null ,注意 key 为 null 只能有一个,value 为 null ,可以多个。
- Map的增删改查 put,remove,replace,遍历所有的key,遍历所有的value,遍历所有的key,value
- k-v 放在 Hash Map$Node node = new Node(hash, key, value, null) 里面
- k-v 为了方便程序员的遍历,还会 创建 Entry Set 集合 ,该集合存放的元素的类型 Entry, 而一个Entry对象就有k,v Entry Set<Entry<K,V>> 即: transient Set<Map. Entry<K,V>> entry Set
Hash Map
- Map接口的常用实现类: Hash Map、 Hash Table和 Properties
- Hash Map是以 key-val对的方式来存储数据( Hash Map$Node类型)
- key不能重复,但是值可以重复允许使用null键建和null值
- 如果添加相同的key,则会覆盖原来的key-val,等同于修改.(key不会替换,val会替换)
- 与 Hash Set一样,不保证映射的顺序,因为底层是以hash表的方式来存储的.(JDK8的
Hash Map底层数组+链表+红黑树) - Hash Map没有实现同步,因此是线程不安全的方法没有做同步互斥的操作,没有synchronized
//初始化加载因子0.75f
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
//执行 put 调用 hash 方法,计算 key 的 hash 值 (h = key.hashCode()) ^ (h >>> 16)
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
////如果底层的 table 数组为 null, 或者 length =0 , 就扩容到 16
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//取出 hash 值对应的 table 的索引位置的 Node, 如果为 null, 就直接把加入的 k-v创建成一个 Node ,加入该位置即可
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
// 如果 table 的索引位置的 key 的 hash 相同和新的 key 的 hash 值相同,
// 并满足(table 现有的结点的 key 和准备添加的 key 是同一个对象|| equals 返回真)就认为不能加入新的 k-v
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
//如果 table 为 null ,或者大小还没有到 64,暂时不树化,而是进行扩容.
//否则才会真正的树化 -> 剪枝
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
- Hash Mapl底层维护了Node类型的数组tabe,默认为null
- 当创建对象时,将加载因子( load factor)初始化为075
- 当添加 key-val时,通过key的哈希值得到在tabel的索引。然后判断该索引处是否有元素,
如果没有元素直接添加。如果该索引处有元素,继续判断该元素的key和准备加入的key相
是否等,如果相等,则直接替换val;如果不相等需要判断是树结构还是链表结构,做出相
应处理。如果添加时发现容量不够,则需要扩容。 - 第1次添加,则需要扩容 table容量为16,临界值( threshold)为12(16*0.75)
- 以后再扩容,则需要扩容 table容量为原来的2倍(32),临界值为原来的2倍即24依次类推
- 在Java8中如果一条链表的元素个数超过 TREEIFY THRESHOLD(默认是8),
- 并且table的大小>= MIN TREEIFY CAPACITY(默认64)就会进行树化红黑树)
Linked Hash Map
Tree Map
- Tree Map存储K-V键值对,通过红黑树(R-B tree)实现
- Tree Map继承了Navigable Map接口,Navigable Map接口继承了Sorted Map接口,可支持排序方法提供了接口,需要Tree Map自己去实现;
- TreeMap实现了Cloneable接口,可被克隆,实现了Serializable接口,可序列化;
- Tree Map因为是通过红黑树实现,红黑树结构天然支持排序,默认情况下通过Key值的自然顺序进行排序;
//构造器. 把传入的实现了 Comparator接口的匿名内部类(对象),传给给TreeMap的comparator
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
//第一次添加, 把k-v 封装到 Entry对象,放入root
Entry<K,V> t = root;
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
Comparator<? super K> cpr = comparator;
if (cpr != null) {
do { //遍历所有的key , 给当前key找到适当位置
parent = t;
//动态绑定到我们的匿名内部类的compare
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
//如果遍历过程中,发现准备添加Key 和当前已有的Key 相等,就不添加
else
return t.setValue(value);
} while (t != null);
}
Hash Table
- 存放的元素是键值对:即K-V
- Hash table的键和值都不能为nu,否则会抛出 Null Pointer Exception
- Hash Table使用方法基本上和 Hash Map一样
- Hash Table是线程安全的( synchronized), Hash Map是线程不安全的
- Hash Table 的元素是头插法,也就是插入到链表的头部,因为Hash Table 是线程安全的,在这个前提下,使用头查法性能更好,否则还有遍历到链表的尾部插入
- Hash Table扩容时,将容量变为原来的2倍加1,而Hash Map扩容时,将容量变为原来的2倍。
//底层有数组 Hashtable$Entry[] 初始化大小为 11
//初始化大小11
public Hashtable() {
this(11, 0.75f);
}
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
//初始化的表
Entry<?,?> tab[] = table;
//计算出key的hash值
int hash = key.hashCode();
// 计算下标 HashMap 是计算key的hash再与tab.length-1进行与运算;
// HashTable则是key的hash值与0x7FFFFFFF进行与运算,然后再对tab.length取模
// 先hash&0x7FFFFFFF后,再对length取模,与0x7FFFFFFF的目的是为了将负的hash值转化为正值,因为hash值有可能为负数,而 &0x7FFFFFFF后,只有符号外改变,而后面的位都不变
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
// 确定 index 位置上的链表头,这里主要是遍历链表找到key 值相等的节点,然后返回old value,这样的话就不用添加新值
// 也就是不用调用addEntry 方法
Entry<K,V> entry = (Entry<K,V>)tab[index];
// 存在key
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
// 链表中不存在,则添加新值
addEntry(hash, key, value, index);
return null;
}
private void addEntry(int hash, K key, V value, int index) {
Entry<?,?> tab[] = table;
//判断是否要扩容
if (count >= threshold) {
// Rehash the table if the threshold is exceeded
rehash();
tab = table;
hash = key.hashCode();
index = (hash & 0x7FFFFFFF) % tab.length;
}
Entry<K,V> e = (Entry<K,V>) tab[index];
// e 也就是 tab[index] 是这个链表的头结点, tab[index] = new Entry<>(hash, key, value, e); 也就是将元素添加到链表的头部,e 做为new Entry<>(hash, key, value, e)的next 节点
tab[index] = new Entry<>(hash, key, value, e);
count++;
modCount++;
protected void rehash() {
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
// overflow-conscious code
// 扩容X2+1
int newCapacity = (oldCapacity << 1) + 1;
// 判断是否超出了容量限制
if (newCapacity - MAX_ARRAY_SIZE > 0) {
if (oldCapacity == MAX_ARRAY_SIZE)
// Keep running with MAX_ARRAY_SIZE buckets
return;
// 最大容量 MAX_ARRAY_SIZE
newCapacity = MAX_ARRAY_SIZE;
}
// 创建新的数组
Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
modCount++;
// 更新 threshold
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
table = newMap;
// 数据迁移,遍历数组
for (int i = oldCapacity ; i-- > 0 ;) {
// for 循环的方式遍历链表
for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
Entry<K,V> e = old;
old = old.next;
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = (Entry<K,V>)newMap[index];
newMap[index] = e;
}
}
}
泛型
泛型注意事项
- interface接口
{ }和class类 - 其中T,K,V不代表值,而是表示类型。
- 任意字母都可以。常用T表示,是Type的缩写
- 在给泛型指定具体类型后,可以传入该类型或者其子类类型
- 普通成员可以使用泛型(属性、方法)
- 使用泛型的数组,不能初始化
- 静态方法中不能使用类的泛型
- 泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)
泛型的继承和通配符
- 泛型不具备继承性
List

浙公网安备 33010602011771号