小建儿的小站


  • 首页

  • 关于

  • 标签

  • 分类

  • 搜索

java程序获取git版本信息

发表于 2018-06-25 | 分类于 java |
字数统计: | 阅读时长 ≈

  临近系统上线,总是频繁的更新jar包,导致并不知道测试环境正在运行哪个版本的程序,所以需要在程序中打出版本信息,这时候可以借助maven插件实现该功能。

git-commit-id-plugin

  git-commit-id-plugin插件,该插件可以获取git分支、版本号、提交时间等信息,该插件的中文文档较少,具体细节可以参考官方github: maven-git-commit-id-plugin

  在我的程序中,配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
<version>2.2.4</version>
<executions>
<execution>
<id>get-the-git-infos</id>
<goals>
<goal>revision</goal>
</goals>
</execution>
</executions>
<configuration>
<dotGitDirectory>${project.basedir}/.git</dotGitDirectory>
<prefix>git</prefix>
<verbose>false</verbose>
//是否单独生成properties文件
<generateGitPropertiesFile>true</generateGitPropertiesFile>
//生成文件路径
<generateGitPropertiesFilename>${project.build.outputDirectory}/git.properties</generateGitPropertiesFilename>
<format>json</format>
<gitDescribe>
<skip>false</skip>
<always>false</always>
<dirty>-dirty</dirty>
</gitDescribe>
</configuration>
</plugin>

templating-maven-plugin

  已经生成了properties文件,在程序中读取该文件进行输出就可以完成任务,在开发环境中确实如此,但是到了线上环境输出却变成了null,最后也没找到原因。所以我采取了替代方案,产生version.java代码。
  templating-maven-plugin插件可以自动编译/src/main/java-templates文件夹下的代码模版,并替换其中的变量,我的模版如下

1
2
3
4
5
6
7

public class Version {
public static final String BRANCH = "${git.branch}";
public static final String VERSION = "${git.build.version}";
public static final String COMMIT_ID = "${git.commit.id}";
public static final String COMMIT_TIME = "${git.commit.time}";
}

  mvn compile后该类回出现在generate-resources文件夹下,可以被直接引用,从而实现输出版本号的功能。

1
2
3
4
5

logger.info("ferrari branch: {}", Version.BRANCH);
logger.info("ferrari version: {}", Version.VERSION);
logger.info("ferrari commit time: {}", Version.COMMIT_TIME);
logger.info("ferrari commit id: {}", Version.COMMIT_ID);

java源码解读--hashtable

发表于 2018-06-21 | 分类于 java |
字数统计: | 阅读时长 ≈

HashTable

HashTable类早期jdk对哈希表的实现,后续加入了HashMap等新的哈希表实现,目前HashTable已经被建议弃用。

HashTable和HashMap的区别有以下几点:

  1. HashTable基于Dictionary抽象类实现了Map接口,而HashMap基于AbstractMap实现了Map接口
  2. HashMap允许key和value为null,而HashTable中不允许
  3. HashTable是线程安全的,HashMap不是,但Collections类中提供饿了synchronizedMap方法对HashMap进行线程安全封装

类定义

继承了Dictionary抽象类,实现了

1
2
3
4

public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable

成员变量

1
2
3
4
5
6
7
8
9
10
//底层存储数组
private transient Entry<?,?>[] table;
//哈希表大小
private transient int count;
//扩容阈值
private int threshold;
//负载因子
private float loadFactor;
//修改次数
private transient int modCount = 0;

构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//传入初始容量和负载因子
public Hashtable(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal Load: "+loadFactor);

if (initialCapacity==0)
initialCapacity = 1;
this.loadFactor = loadFactor;
table = new Entry<?,?>[initialCapacity];
threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
}

//默认负载因子是0.75
public Hashtable(int initialCapacity) {
this(initialCapacity, 0.75f);
}
//默认初始容量是11,负载因子是0.75
public Hashtable() {
this(11, 0.75f);
}

public Hashtable(Map<? extends K, ? extends V> t) {
this(Math.max(2*t.size(), 11), 0.75f);
putAll(t);
}

put

方法被synchronized修饰,线程安全。
对value进行进行非空检查,若为空,报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

public synchronized V put(K key, V value) {
//value为空报错
if (value == null) {
throw new NullPointerException();
}

Entry<?,?> tab[] = table;
//若key为空,这里会报空指针异常
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
//检查是否需要覆盖
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;
}

get

get方法也被synchronized修饰,线程安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

public synchronized V get(Object key) {
Entry<?,?> tab[] = table;
//key为null这里会报空指针异常
int hash = key.hashCode();
//计算下标
int index = (hash & 0x7FFFFFFF) % tab.length;
//查找key
for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
return (V)e.value;
}
}
return null;
}

remove

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
	
public synchronized V remove(Object key) {
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>)tab[index];
for(Entry<K,V> prev = null ; e != null ; prev = e, e = e.next) {
if ((e.hash == hash) && e.key.equals(key)) {
modCount++;
if (prev != null) {
prev.next = e.next;
} else {
tab[index] = e.next;
}
count--;
V oldValue = e.value;
e.value = null;
return oldValue;
}
}
return null;
}

扩容相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

private void addEntry(int hash, K key, V value, int index) {
modCount++;

Entry<?,?> tab[] = table;
//如果元素个数超过扩容阈值
if (count >= threshold) {
//创建新数组,并把老数组元素复制过来
rehash();

tab = table;
hash = key.hashCode();
index = (hash & 0x7FFFFFFF) % tab.length;
}

// Creates the new entry.
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>) tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
}

protected void rehash() {
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;

//扩容一倍+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;
newCapacity = MAX_ARRAY_SIZE;
}
Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];

modCount++;
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
table = newMap;
//按照顺序依此复制
for (int i = oldCapacity ; i-- > 0 ;) {
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;
}
}
}

java源码解读--weakhashmap

发表于 2018-06-21 | 分类于 java |
字数统计: | 阅读时长 ≈

WeakHashMap

WeakHasMap是针对弱引用的一个类,深入理解java虚拟机中对java引用概念进行了详细说明,在此不做重复解释。需要注意的是弱引用只存活到下一次垃圾回收之前,WeakHasMap中的键都是弱引用类型,所以该类可以自动移除已经被回收的键所对应的k-v对。

  • 该类支持null值输入
  • 默认每次扩容一倍
  • fail-fast

类定义

继承了AbstractMap类,实现了Map接口,注意该类没有实现cloneable、serializable接口

1
2
3
public class WeakHashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V> {

类变量和成员变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//默认初始容量
private static final int DEFAULT_INITIAL_CAPACITY = 16;
//最大容量
private static final int MAXIMUM_CAPACITY = 1 << 30;
//默认负载因子
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
//底层存储结构
Entry<K,V>[] table;
//数组大小
private int size;
//扩容阈值
private int threshold;
//负载因子
private final float loadFactor;
//referencequeue用于弱引用的回收
private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
//修改次数
int modCount;

构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

public WeakHashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Initial Capacity: "+
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;

if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal Load factor: "+
loadFactor);
//大于initialCapacity最小的2的幂次
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
//初始化table
table = newTable(capacity);
//负载因子赋值
this.loadFactor = loadFactor;
//计算扩容阈值
threshold = (int)(capacity * loadFactor);
}

public WeakHashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}

public WeakHashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}


public WeakHashMap(Map<? extends K, ? extends V> m) {
this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
DEFAULT_INITIAL_CAPACITY),
DEFAULT_LOAD_FACTOR);
putAll(m);
}

entry

WeakHashMap中的entry继承了WeakReference,可以在gc时放入ReferenceQueue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
   private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
V value;
final int hash;
Entry<K,V> next;

/**
* Creates new entry.
*/
Entry(Object key, V value,
ReferenceQueue<Object> queue,
int hash, Entry<K,V> next) {
//这里把只关联了key,只有当key被回收时,entry才会被放入ReferenceQueue
super(key, queue);
this.value = value;
this.hash = hash;
this.next = next;
}

......

}

put

put函数会自动调用移除已释放引用相关方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

public V put(K key, V value) {
//null值统一使用NULL_KEY代替
Object k = maskNull(key);
//获取hash值
int h = hash(k);
//getTable方法会调用expungeStaleEntries()方法进行已释放引用的移除
Entry<K,V>[] tab = getTable();
//计算下标
int i = indexFor(h, tab.length);
//查找是否已经存储过该key
for (Entry<K,V> e = tab[i]; e != null; e = e.next) {
if (h == e.hash && eq(k, e.get())) {
V oldValue = e.value;
if (value != oldValue)
e.value = value;
return oldValue;
}
}

modCount++;
//table不存在该key,则新建entry并放入对应下标位置
Entry<K,V> e = tab[i];
tab[i] = new Entry<>(k, value, queue, h, e);
//如果容量不够,扩容一倍
if (++size >= threshold)
resize(tab.length * 2);
return null;
}

get

get方法也会自动调用清除已释放引用相关的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14

public V get(Object key) {
Object k = maskNull(key);
int h = hash(k);
Entry<K,V>[] tab = getTable();
int index = indexFor(h, tab.length);
Entry<K,V> e = tab[index];
while (e != null) {
if (e.hash == h && eq(k, e.get()))
return e.value;
e = e.next;
}
return null;
}

expungeStaleEntries

该方法用于移除已释放元素,该类中很多方法会间接调用该方法,如size,getTable,resize,get,put

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

private void expungeStaleEntries() {
//已经释放的弱引用会加入ReferenceQueue中,所以遍历queue
for (Object x; (x = queue.poll()) != null; ) {
//同步操作
synchronized (queue) {
@SuppressWarnings("unchecked")
//从queue取出来的是entry,这点由entry定义决定
Entry<K,V> e = (Entry<K,V>) x;
//计算下标
int i = indexFor(e.hash, table.length);
//移除逻辑
Entry<K,V> prev = table[i];
Entry<K,V> p = prev;
while (p != null) {
Entry<K,V> next = p.next;
if (p == e) {
if (prev == e)
table[i] = next;
else
prev.next = next;
// Must not null out e.next;
// stale entries may be in use by a HashIterator
e.value = null; // Help GC
size--;
break;
}
prev = p;
p = next;
}
}
}
}

resize

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//需传入扩容后容量
void resize(int newCapacity) {
//此处会调用expungeStaleEntries
Entry<K,V>[] oldTable = getTable();
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
//创建新数组
Entry<K,V>[] newTable = newTable(newCapacity);
//复制数据,新的顺序是原顺序的反向
transfer(oldTable, newTable);
table = newTable;

if (size >= threshold / 2) {
threshold = (int)(newCapacity * loadFactor);
} else {
expungeStaleEntries();
transfer(newTable, oldTable);
table = oldTable;
}
}

java源码解读--treemap

发表于 2018-06-13 | 分类于 java |
字数统计: | 阅读时长 ≈

TreeMap

treemap可以根据key的大小存储k-v对,从而保证元素有序。treemap底层使用红黑树结构来实现。

类定义

继承了AbstractMap抽象类,实现了NavigableMap接口

1
2
3
public class TreeMap<K,V>
extends AbstractMap<K,V>
implements NavigableMap<K,V>, Cloneable, java.io.Serializable

成员变量

1
2
3
4
5
6
7
8
//比较器
private final Comparator<? super K> comparator;
//头节点
private transient Entry<K,V> root;
//k-v对数量
private transient int size = 0;
//修改次数
private transient int modCount = 0;

构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public TreeMap() {
comparator = null;
}

public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}

public TreeMap(Map<? extends K, ? extends V> m) {
comparator = null;
putAll(m);
}

public TreeMap(SortedMap<K, ? extends V> m) {
comparator = m.comparator();
try {
buildFromSorted(m.size(), m.entrySet().iterator(), null, null);
} catch (java.io.IOException cannotHappen) {
} catch (ClassNotFoundException cannotHappen) {
}
}

entry

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//treemap中entry的定义,除了k、v还有左右父节点指针,颜色标志位
static final class Entry<K,V> implements Map.Entry<K,V> {
K key;
V value;
Entry<K,V> left;
Entry<K,V> right;
Entry<K,V> parent;
boolean color = BLACK;

Entry(K key, V value, Entry<K,V> parent) {
this.key = key;
this.value = value;
this.parent = parent;
}

public K getKey() {
return key;
}

public V getValue() {
return value;
}

public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue;
}

public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;

return valEquals(key,e.getKey()) && valEquals(value,e.getValue());
}

public int hashCode() {
int keyHash = (key==null ? 0 : key.hashCode());
int valueHash = (value==null ? 0 : value.hashCode());
return keyHash ^ valueHash;
}

public String toString() {
return key + "=" + value;
}
}

put

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

public V put(K key, V value) {
Entry<K,V> t = root;
//root是null,则构建新entry作为root节点
if (t == null) {
compare(key, key); // type (and possibly null) check

root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator;
if (cpr != null) {
//如果设置了比较器,则循环查找插入点,出现相等情况则覆盖原始值
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
else {
//如果没有设置比较器,则使用对象内部的compareTo方法进行比较
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
//找到插入位置,构造新的entry
Entry<K,V> e = new Entry<>(key, value, parent);
//判断插入左节点还是右节点
if (cmp < 0)
parent.left = e;
else
parent.right = e;
//红黑树进行调整
fixAfterInsertion(e);
size++;
modCount++;
return null;
}

fixAfterInsertion

在红黑树中插入节点后,会让红黑树变得不平衡,需要调用该方法调整树结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

private void fixAfterInsertion(Entry<K,V> x) {
//默认所有新插入元素为红色节点,这样不违反红黑树从根节点到叶子节点所有路径包含的黑色节点个数相同的性质
x.color = RED;
//3种情况满足一点就结束调整
//1.当前节点为空
//2.当前节点是root
//3.当前节点的父节点是黑色
while (x != null && x != root && x.parent.color == RED) {
//父节点是祖父节点的左节点
if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
//y是祖父节点的右节点,即叔父节点
Entry<K,V> y = rightOf(parentOf(parentOf(x)));
//如果叔父节点是红色
if (colorOf(y) == RED) {
//父节点颜色置为黑色
setColor(parentOf(x), BLACK);
//叔父节点颜色置为黑色
setColor(y, BLACK);
//祖父节点置为红色
setColor(parentOf(parentOf(x)), RED);
//重新冲祖父节点调整红黑树
x = parentOf(parentOf(x));
} else {
//如果叔父节点不存在或者为黑色
//如果当前节点是父节点的右节点
if (x == rightOf(parentOf(x))) {
x = parentOf(x);
//以当前节点的父节点为支点进行左旋
rotateLeft(x);
}
//设置父节点为黑色
setColor(parentOf(x), BLACK);
//设置祖父节点为红色
setColor(parentOf(parentOf(x)), RED);
//以祖父节点为支点进行右旋
rotateRight(parentOf(parentOf(x)));
}
} else {
//如果当前节点的左节点是祖父节点的右节点
//y是祖父节点的左节点,即当前节点的叔父节点
Entry<K,V> y = leftOf(parentOf(parentOf(x)));
//叔父节点是红色,直接修改颜色
if (colorOf(y) == RED) {
setColor(parentOf(x), BLACK);
setColor(y, BLACK);
setColor(parentOf(parentOf(x)), RED);
x = parentOf(parentOf(x));
} else {
//如果x是父节点的左节点
if (x == leftOf(parentOf(x))) {
x = parentOf(x);
//以父节点为支点左右旋
rotateRight(x);
}
//设置父节点为黑色
setColor(parentOf(x), BLACK);
//设置祖父节点为红色
setColor(parentOf(parentOf(x)), RED);
//以祖父节点为支点进行左旋
rotateLeft(parentOf(parentOf(x)));
}
}
}
root.color = BLACK;
}

get

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
   public V get(Object key) {
Entry<K,V> p = getEntry(key);
return (p==null ? null : p.value);
}
//直接根据排序查找
final Entry<K,V> getEntry(Object key) {
//如果默认比较器不为空,则使用默认比较器进行比较
if (comparator != null)
return getEntryUsingComparator(key);
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
//默认比较器为空,则使用compareTo方法进行比较
Entry<K,V> p = root;
while (p != null) {
int cmp = k.compareTo(p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
return null;
}

remove

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

public V remove(Object key) {
//先找到对应的entry
Entry<K,V> p = getEntry(key);
if (p == null)
return null;
//返回原value
V oldValue = p.value;
//删除entry
deleteEntry(p);
return oldValue;
}

//删除entry的真正实现
private void deleteEntry(Entry<K,V> p) {
modCount++;
size--;
//若被删除节点p的左孩子和右孩子都不为空,则查找替代节点
if (p.left != null && p.right != null) {
//查找p的替代节点,一般为p的右子树中最小的节点
Entry<K,V> s = successor(p);
p.key = s.key;
p.value = s.value;
//后继节点变成待删除节点,由于只是替换,没有真正删除节点,所以前面的树结构不用改变
p = s;
} // p has 2 children

//replacement是替代节点的后继节点
Entry<K,V> replacement = (p.left != null ? p.left : p.right);
//如果待删除节点p只有一个后继节点
if (replacement != null) {
//把p的父节点给replacement节点
replacement.parent = p.parent;
//如果p是根节点,则replacement为根节点
if (p.parent == null)
root = replacement;
else if (p == p.parent.left)
p.parent.left = replacement;
else
p.parent.right = replacement;
//接触待删除节点的前后引用关系
p.left = p.right = p.parent = null;

//如果替代节点p的节点为黑色,需要调整红黑树的结构
if (p.color == BLACK)
fixAfterDeletion(replacement);
} else if (p.parent == null) { // return if we are the only node.
root = null;
} else {
//p没有子节点
//如果p是黑色节点,调整红黑树
if (p.color == BLACK)
fixAfterDeletion(p);
//删除p
if (p.parent != null) {
//解除父节点对p的饮用
if (p == p.parent.left)
p.parent.left = null;
else if (p == p.parent.right)
p.parent.right = null;
//解除p对父节点的饮用
p.parent = null;
}
}
}

fixAfterDeletion

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//被删除节点的为黑色时才需要进行结构调整 
private void fixAfterDeletion(Entry<K,V> x) {
//如果x不是跟节点并且x的颜色是黑色
while (x != root && colorOf(x) == BLACK) {
//如果x是父节点的左节点
if (x == leftOf(parentOf(x))) {
//sib代表兄弟节点
Entry<K,V> sib = rightOf(parentOf(x));
//如果兄弟节点是红色,删除节点后导致各条路径中黑色节点个数不一样,需要进行旋转操作
if (colorOf(sib) == RED) {
setColor(sib, BLACK);
setColor(parentOf(x), RED);
rotateLeft(parentOf(x));
//兄弟节点设置为父节点的右节点
sib = rightOf(parentOf(x));
}
//如果兄弟节点的两个子节点都是黑色,此时parentof(x)一定是红色,不管经不经过第一个if的调整
if (colorOf(leftOf(sib)) == BLACK &&
colorOf(rightOf(sib)) == BLACK) {
//把兄弟节点设置为红色
setColor(sib, RED);
//x更新为x的父节点
x = parentOf(x);
} else {
//兄弟节点的两个自节点不都是黑色
//如果兄弟节点的右节点是黑色
if (colorOf(rightOf(sib)) == BLACK) {
//把兄弟节点的左节点设置为黑色
setColor(leftOf(sib), BLACK);
//把兄弟节点设置为红色
setColor(sib, RED);
//以兄弟节点为支点进行右旋
rotateRight(sib);
//把兄弟节点设置为x的右节点
sib = rightOf(parentOf(x));
}
//如果兄弟节点的两个子节点都是红色
//把兄弟节点颜色设置为父节点颜色
setColor(sib, colorOf(parentOf(x)));
//把父节点设置为黑色
setColor(parentOf(x), BLACK);
//把兄弟节点的右节点设置为黑色
setColor(rightOf(sib), BLACK);
//以父节点为支点进行左旋
rotateLeft(parentOf(x));
x = root;
}
} else { // symmetric
Entry<K,V> sib = leftOf(parentOf(x));

if (colorOf(sib) == RED) {
setColor(sib, BLACK);
setColor(parentOf(x), RED);
rotateRight(parentOf(x));
sib = leftOf(parentOf(x));
}

if (colorOf(rightOf(sib)) == BLACK &&
colorOf(leftOf(sib)) == BLACK) {
setColor(sib, RED);
x = parentOf(x);
} else {
if (colorOf(leftOf(sib)) == BLACK) {
setColor(rightOf(sib), BLACK);
setColor(sib, RED);
rotateLeft(sib);
sib = leftOf(parentOf(x));
}
setColor(sib, colorOf(parentOf(x)));
setColor(parentOf(x), BLACK);
setColor(leftOf(sib), BLACK);
rotateRight(parentOf(x));
x = root;
}
}
}

setColor(x, BLACK);
}

java源码解读--enummap

发表于 2018-06-13 | 分类于 java |
字数统计: | 阅读时长 ≈

EnumMap

  1. EnumMap要求key必须为枚举类,且创建EnumMap时必须指定key的类型
  2. key不能为null,但是value允许
  3. 底层为数组结构
  4. 元素顺序为Enum顺序,与插入顺序无关
  5. 非线程安全,不会抛出ConcurrentModificationException

类定义

继承AbstractMap

1
2
3

public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V>
implements java.io.Serializable, Cloneable

成员变量

1
2
3
4
5
6
7
8
//key的类型
private final Class<K> keyType;
//存放key值,该值在初始化时就已经从枚举类中读入
private transient K[] keyUniverse;
//存放value值
private transient Object[] vals;
//数组大小
private transient int size = 0;

put

1
2
3
4
5
6
7
8
9
10
11
12
13
14

public V put(K key, V value) {
//检查key必须是keyType或者其子类,否则报错
typeCheck(key);
//ordinal用于返回该枚举值在枚举类中的顺序,根据声明顺序而定,此处以该值作为下标
int index = key.ordinal();
//取出旧值
Object oldValue = vals[index];
//如果value为null,此处maskNull会返回一个固定的Object来代替
vals[index] = maskNull(value);
if (oldValue == null)
size++;
return unmaskNull(oldValue);
}

get

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//先判断是否合法,合法就通过ordinal访问下标,否则返回null
public V get(Object key) {
return (isValidKey(key) ?
unmaskNull(vals[((Enum<?>)key).ordinal()]) : null);
}
//判断key是否合法
private boolean isValidKey(Object key) {
if (key == null)
return false;

//判断类型是否合法
Class<?> keyClass = key.getClass();
return keyClass == keyType || keyClass.getSuperclass() == keyType;
}

remove

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

public V remove(Object key) {
//判断类型是否合法
if (!isValidKey(key))
return null;
//找到下标
int index = ((Enum<?>)key).ordinal();
//获取旧值
Object oldValue = vals[index];
//释放该位置
vals[index] = null;
if (oldValue != null)
size--;
return unmaskNull(oldValue);
}

java源码解读--identityhashmap

发表于 2018-06-10 | 分类于 java |
字数统计: | 阅读时长 ≈

IdentityHashMap

IdentityHashMap不常被用到,我也是这次系统性看集合源码时才发先这个类,感觉这个类的实现比较有意思。

  1. 首先这个类底层没有使用自己特殊的数据结构(hashmap内部使用node,linkedhashmap使用entry等),IdentityHashMap底层直接使用Object数组实现,奇数下标存储键,偶数位下标存储值
  2. identityHashMap使用==来判断两个键是否相等,这个特性使得identityHashMap中的键可以重复
  3. identityHashMap使用System.identityHashCode进行存储位置查找,而HashMap使用Object中的HashCode实现

System.identityHashCode与Object.hashCode区别

  1. hashCode是Object类下的一个方法,子类可以继承、重写该方法,根据对象内存地址计算哈希值。比如String类重写了hashCode方法,并改为根据字符串序列计算哈希值
  2. identityHashCode方法是System类中的静态方法,根据对象内存地址计算哈希值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void testHashCode() {
// a,b是不同的对象
String a = new String("string");
String b = new String("string");
//String类重写了hashcode方法,只要内容一样,hashcode就相等
System.out.println(a.hashCode() + " " + b.hashCode());
//由于两个对象不一样,所以System.identityHashCode结果不一样
System.out.println(System.identityHashCode(a) + " " + System.identityHashCode(b));

//c,d是一个对象,因为没有使用new String,所以"string"是常量,存在于常量池中
String c = "string";
String d = "string";
//c,d字符串一样,所以哈希吗一样
System.out.println(c.hashCode() + " " + d.hashCode());
//由于是同一个常量,所以identityHashCode一样
System.out.println(System.identityHashCode(c) + " " + System.identityHashCode(d));
}

类定义

该类继承了AbstractMap,实现了Map接口

1
2
3
4

public class IdentityHashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, java.io.Serializable, Cloneable

重要的成员变量和类变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//默认初始大小,必须是2的幂次,
private static final int DEFAULT_CAPACITY = 32;
//最小容量
private static final int MINIMUM_CAPACITY = 4;
//最大容量
private static final int MAXIMUM_CAPACITY = 1 << 29;
//存储结构
transient Object[] table; // non-private to simplify nested class access
//键值对个数
int size;
//修改次数
transient int modCount;
//null key对应的value
static final Object NULL_KEY=new Object();

构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

public IdentityHashMap() {
//默认初始容量
init(DEFAULT_CAPACITY);
}

public IdentityHashMap(int expectedMaxSize) {
if (expectedMaxSize < 0)
throw new IllegalArgumentException("expectedMaxSize is negative: "
+ expectedMaxSize);
//使用传入的容量大小
init(capacity(expectedMaxSize));
}

public IdentityHashMap(Map<? extends K, ? extends V> m) {
// Allow for a bit of growth
//扩容10%
this((int) ((1 + m.size()) * 1.1));
putAll(m);
}

capacity

此函数返回最小的大于expectedMaxSize的2次幂

1
2
3
4
5
6
7
private static int capacity(int expectedMaxSize) {
// assert expectedMaxSize >= 0;
return
(expectedMaxSize > MAXIMUM_CAPACITY / 3) ? MAXIMUM_CAPACITY :
(expectedMaxSize <= 2 * MINIMUM_CAPACITY / 3) ? MINIMUM_CAPACITY :
Integer.highestOneBit(expectedMaxSize + (expectedMaxSize << 1));
}

hash

使用System.identityHasCode求哈希值

1
2
3
4
5
6
//返回对应的下标index
private static int hash(Object x, int length) {
int h = System.identityHashCode(x);
// Multiply by -127, and left-shift to use least bit as part of hash
return ((h << 1) - (h << 8)) & (length - 1);
}

put

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
   public V put(K key, V value) {
//如果key==null,返回一个固定的Object对象作为键
final Object k = maskNull(key);

retryAfterResize: for (;;) {
final Object[] tab = table;
final int len = tab.length;
//求元素对应的数组下标值
int i = hash(k, len);
//如果发生hash冲突,会调用nextKeyIndex向后探测没有使用的位置
for (Object item; (item = tab[i]) != null;
i = nextKeyIndex(i, len)) {
//此处注意,判断键是否相等使用==,不是用equals
if (item == k) {
@SuppressWarnings("unchecked")
//冲突则把value替换,返回旧值
V oldValue = (V) tab[i + 1];
tab[i + 1] = value;
return oldValue;
}
}
//更新size
final int s = size + 1;
// Use optimized form of 3 * s.
// Next capacity is len, 2 * current capacity.
if (s + (s << 1) > len && resize(len))
continue retryAfterResize;

modCount++;
//key放在i
tab[i] = k;
//value放在i+1
tab[i + 1] = value;
size = s;
return null;
}
}

get

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

public V get(Object key) {
//如果key是null,返回null对应的Object对象
Object k = maskNull(key);
Object[] tab = table;
int len = tab.length;
//查找key对应的下标
int i = hash(k, len);
//遍历table,由于采用向后探测的方式解决hash冲突,所以这里回循环找下去
while (true) {
Object item = tab[i];
//找到对应key,返回value
if (item == k)
return (V) tab[i + 1];
if (item == null)
return null;
//若没有找到,有可能发生hash冲突,向后寻找
i = nextKeyIndex(i, len);
}
}

nextKeyIndex

向后探测逻辑比较简单,每次移动一个key-value的位置,即2个index

1
2
3
4
private static int nextKeyIndex(int i, int len) {
// 往后移两个单位
return (i + 2 < len ? i + 2 : 0);
}

resize

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

private boolean resize(int newCapacity) {
// assert (newCapacity & -newCapacity) == newCapacity; // power of 2
//扩容一倍
int newLength = newCapacity * 2;

Object[] oldTable = table;
int oldLength = oldTable.length;
//判断新容量是否合法
if (oldLength == 2 * MAXIMUM_CAPACITY) { // can't expand any further
if (size == MAXIMUM_CAPACITY - 1)
throw new IllegalStateException("Capacity exhausted.");
return false;
}
if (oldLength >= newLength)
return false;

Object[] newTable = new Object[newLength];
//把oldTable里的元素重新放入newTable
for (int j = 0; j < oldLength; j += 2) {
Object key = oldTable[j];
if (key != null) {
Object value = oldTable[j+1];
oldTable[j] = null;
oldTable[j+1] = null;
//重新计算老元素在newTable中的index
int i = hash(key, newLength);
//发生冲突,向后探测
while (newTable[i] != null)
i = nextKeyIndex(i, newLength);
newTable[i] = key;
newTable[i + 1] = value;
}
}
table = newTable;
return true;
}

remove

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  public V remove(Object key) {
Object k = maskNull(key);
Object[] tab = table;
int len = tab.length;
int i = hash(k, len);

while (true) {
Object item = tab[i];
if (item == k) {
modCount++;
size--;
@SuppressWarnings("unchecked")
V oldValue = (V) tab[i + 1];
//删除value
tab[i + 1] = null;
//删除key
tab[i] = null;
//该位置上的元素被删除后查找后面是否有元素可以被往前移动
closeDeletion(i);
return oldValue;
}
if (item == null)
return null;
i = nextKeyIndex(i, len);
}
}

closeDeletion

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

private void closeDeletion(int d) {
Object[] tab = table;
int len = tab.length;

Object item;
//查看下一个key的位置上元素是否满足移动要求
for (int i = nextKeyIndex(d, len); (item = tab[i]) != null;
i = nextKeyIndex(i, len) ) {
//求下一个元素的哈希值
int r = hash(item, len);
//如果出现过hash冲突,并且符合条件,把冲突的键值对向前移动
if ((i < r && (r <= d || d <= i)) || (r <= d && d <= i)) {
tab[d] = item;
tab[d + 1] = tab[i + 1];
tab[i] = null;
tab[i + 1] = null;
d = i;
}
}
}

java源码解读--linkedhashmap

发表于 2018-06-08 | 分类于 java |
字数统计: | 阅读时长 ≈

LinkedHashMap

HashMap存储元素是无序的,而LinkedHashMap在hashmap的基础上,能够保持元素的有序性,并且能够基于LinkedHashMap实现LRU缓存

类定义

继承了HashMap类,实现了Map接口,所以LinkedHashMap大部分功能与HashMap一致,在保持元素顺序上有区别。

1
2
3
public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>

类成员变量

1
2
3
4
5
6
//entry头指针
transient LinkedHashMap.Entry<K,V> head;
//entry尾指针
transient LinkedHashMap.Entry<K,V> tail;
//是否按照访问顺序排序
final boolean accessOrder;

构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//初始容量和负载因子,排序标志默认为关闭
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
}
//初始容量,负载因子默认0.75,排序标志默认关闭
public LinkedHashMap(int initialCapacity) {
super(initialCapacity);
accessOrder = false;
}
//初始容量默认16,负载因子默认为0.75,排序标志默认关闭
public LinkedHashMap() {
super();
accessOrder = false;
}
//初始化输入map
public LinkedHashMap(Map<? extends K, ? extends V> m) {
super();
accessOrder = false;
putMapEntries(m, false);
}
//初始容量、负载因子、是否按照访问顺序排序,基于linkedhashmap的lru实现会调用该构造方法
public LinkedHashMap(int initialCapacity,float loadFactor,boolean accessOrder) {
super(initialCapacity, ladFactor);
this.accessOrder = accessOrder;
}

entry定义

1
2
3
4
5
6
7
   static class Entry<K,V> extends HashMap.Node<K,V> {
//在hashmap.node的基础上增加了前后指针
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}

建立链表

LinkedHashMap添加元素操作复用了HashMap的put()方法,但是重写了以下两个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
   Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
//新建Entry
LinkedHashMap.Entry<K,V> p =
new LinkedHashMap.Entry<K,V>(hash, key, value, e);
//链表操作,新加入的元素放作为tail
linkNodeLast(p);
return p;
}

private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
LinkedHashMap.Entry<K,V> last = tail;
//把新节点p作为tail
tail = p;
//如果原来的tail==null,说明链表中没有元素,将p设置为头节点
if (last == null)
head = p;
else {
//如果链表中有元素,则把原来最后一个元素的后项指针指向p,p的前向指针指向last
p.before = last;
last.after = p;
}
}

删除链表节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
   void afterNodeRemoval(Node<K,V> e) { 
//待删除节点为p,前驱节点为b,后继节点为a
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
//释放p的前后指针
p.before = p.after = null;
// 如果p的前驱节点为null,
if (b == null)
head = a;
else
//链表中有元素,则直接把p的前驱节点的后继节点设置为p的后继节点
b.after = a;
if (a == null)
//如果后继节点为空,则说明b节点为tail
tail = b;
else
//后继节点不为空,设置a的前驱节点为b
a.before = b;
}

插入节点

此处可能涉及是否移除过期元素的处理

1
2
3
4
5
6
7
8
9
//插入节点后如何维护链表
void afterNodeInsertion(boolean evict) {
LinkedHashMap.Entry<K,V> first;
//removeEldestEntry用来判断是否加入过期策略,移除最老的元素。evict是从hashmap传过来的参数,一般情况下初始化时为false,其他情况为true
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
removeNode(hash(key), key, null, false, true);
}
}

访问顺序维护

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

void afterNodeAccess(Node<K,V> e) {
LinkedHashMap.Entry<K,V> last;
//顺序访问维护标志设置为true,并且e不是最后一个元素
if (accessOrder && (last = tail) != e) {
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
//p的后继节点设置为null
p.after = null;
//p的前驱节点为null,则头节点设置为a
if (b == null)
head = a;
else
//p的前驱节点的后继节点设置为p的后继节点,即删除p
b.after = a;
//p的后继节点存在,设置a的前驱节点为b
if (a != null)
a.before = b;
else
//否则,尾节点为b
last = b;
//如果尾节点为null,则头节点设置为p
if (last == null)
head = p;
else {
//否则,把p设置为last节点后
p.before = last;
last.after = p;
}
//设置尾节点为p
tail = p;
++modCount;
}
}

是否移除过期元素

1
2
3
4
//需要覆盖该方法实现是否移除元素的逻辑
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}

基于LinkedHashMap实现LRUMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

public class LRUMap extends LinkedHashMap{

private int capacity;

public LRUMap(int capacity){
super(16,0.75f,true);
this.capacity = capacity;
}

@Override
public boolean removeEldestEntry(Map.Entry<K,V> eldest){
return size()>capacity;
}

}

java源码解读--vector&stack

发表于 2018-06-05 | 分类于 java |
字数统计: | 阅读时长 ≈

Vector

vector是jdk1.0引入的,底层实现也是可变长数组,和arraylist类似,其区别如下:

  1. vector是线程安全的,对外暴露的方法几乎都加了sychronized关键字
  2. 扩容时默认扩充原数组大小的一倍,而arrayList一次扩容0.5倍

类定义

与arraylist定义一致,继承了abstractList类,实现了list、randomaccess、cloneable、serializable接口。

1
2
3
4

public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable

成员变量

1
2
3
4
5
6
7
8
//用于存储数据的数组
protected Object[] elementData;

//元素个数
protected int elementCount;

//扩容时增加的容量
protected int capacityIncrement;

构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//初始化传入初始容量和扩容容量
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
//传入初始容量,扩容容量默认为0
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
//初始容量默认为10
public Vector() {
this(10);
}

public Vector(Collection<? extends E> c) {
elementData = c.toArray();
elementCount = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}

扩容逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//默认最大容量2的31次方-8
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//如果扩容增加个数大于0,则一次扩容capacityIncrement个;如果capacityIncrement等于0,则一次扩容原来容量的一倍
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}

private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}

elements

该方法返回一个Enumeration,类似迭代器,区别在于iterator可以修改数据(remove方法),而enumeration不能,iterator支持fail-fast机制,enumerator不支持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public Enumeration<E> elements() {
return new Enumeration<E>() {
int count = 0;

public boolean hasMoreElements() {
return count < elementCount;
}

public E nextElement() {
synchronized (Vector.this) {
if (count < elementCount) {
return elementData(count++);
}
}
throw new NoSuchElementException("Vector Enumeration");
}
};
}

其他

vector其他方法与arraylist类似,但是大部分添加了sychronized关键字进行同步,该类目前已弃用,了解一下就好

Stack

stack类实现了栈的功能,即先进后出,也是线程安全的。

类定义

继承了Vector类

1
2
3

public
class Stack<E> extends Vector<E> {

重要方法

实现了peek、pop、push等常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//添加元素
public E push(E item) {
addElement(item);

return item;
}
//取栈顶元素
public synchronized E pop() {
E obj;
int len = size();

obj = peek();
removeElementAt(len - 1);

return obj;
}
//查询栈顶元素
public synchronized E peek() {
int len = size();

if (len == 0)
throw new EmptyStackException();
return elementAt(len - 1);
}

public boolean empty() {
return size() == 0;
}

public synchronized int search(Object o) {
int i = lastIndexOf(o);

if (i >= 0) {
return size() - i;
}
return -1;
}

java源码解读--queue

发表于 2018-06-04 | 分类于 java |
字数统计: | 阅读时长 ≈

Queue接口

queue接口特点:可以模拟队列行为,即“先进先出”。

接口结构

queue接口继承了Collection接口,并增加了一些新方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface Queue<E> extends Collection<E>{

//将元素插入队列,成功返回true,失败抛异常
boolean add(E e);
//将元素插入队列,如果失败返回false
boolean offer(E e);
//移除并返回队列中的第一个元素,队列为空时,抛异常
E remove();
//移除并返回队列中的第一个元素,队列为空时,返回null
E poll();
//返回队列中第一个元素,但不移除,队列为空时,抛异常
E element();
//返回队列中第一个元素,但不移除,对列为空时,返回null
E peek();

}

抽象类AbstractQueue

queue接口中,add与offer、remove与poll、element与peek,功能一致,只是对异常情况的处理不同。AbstractQueue源码中可以看出这些方法处理异常的方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public abstract class AbstractQueue<E>
extends AbstractCollection<E>
implements Queue<E> {

protected AbstractQueue() {
}

//add底层调用offer,当offer失败返回false时,add方法抛出异常
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
//remove底层调用poll,当poll返回null时,remove方法抛出异常
public E remove() {
E x = poll();
if (x != null)
return x;
else
throw new NoSuchElementException();
}
//element底层调用peek,当peek返回null时,element方法抛出异常
public E element() {
E x = peek();
if (x != null)
return x;
else
throw new NoSuchElementException();
}
//clear方法循环调用poll,直到返回为null
public void clear() {
while (poll() != null)
;
}

public boolean addAll(Collection<? extends E> c) {
if (c == null)
throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;
return modified;
}

}

Deque接口

Deque是双端队列,底层由循环数组实现,其继承了Queue接口,并扩展了新特性。
Deque重写了Queue的全部方法,Stack、Collection的部分方法,并增加了对首尾元素处理的相关方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
public interface Deque<E> extends Queue<E> {

void addFirst(E e);

void addLast(E e);

boolean offerFirst(E e);

boolean offerLast(E e);

E removeFirst();

E removeLast();

E pollFirst();

E pollLast();

E getFirst();

E getLast();

E peekFirst();

E peekLast();

boolean removeFirstOccurrence(Object o);

boolean removeLastOccurrence(Object o);

// *** Queue methods ***

boolean add(E e);

boolean offer(E e);

E remove();

E poll();

E element();

E peek();


// *** Stack methods ***

void push(E e);

E pop();


// *** Collection methods ***

boolean remove(Object o);

boolean contains(Object o);

public int size();

Iterator<E> iterator();

Iterator<E> descendingIterator();

}

ArrayDeque

ArrayDeque不可以存取null元素,因为会根据某个位置是否为null来判断元素是否存在。
当作为栈使用时,性能比stack好,作为队列使用时,性能比linkedlist好

类定义

继承了AbstractCollection,实现了Deque接口

1
2
public class ArrayDeque<E> extends AbstractCollection<E>
implements Deque<E>, Cloneable, Serializable

重要的成员变量

1
2
3
4
// 第一个元素的索引  
private transient int head;
// 下个要添加元素的位置,为末尾元素的索引 + 1
private transient int tail;

构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//默认数组大小16
public ArrayDeque() {
elements = new Object[16];
}
//传入数组大小
public ArrayDeque(int numElements) {
//调整实际数组大小为大于传入值的最小的2的幂次
allocateElements(numElements);
}
//直接传入集合
public ArrayDeque(Collection<? extends E> c) {
allocateElements(c.size());
addAll(c);
}

调整数组大小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void allocateElements(int numElements) {  
int initialCapacity = MIN_INITIAL_CAPACITY;
// 找到大于需要长度的最小的2的幂整数。
if (numElements >= initialCapacity) {
initialCapacity = numElements;
initialCapacity |= (initialCapacity >>> 1);
initialCapacity |= (initialCapacity >>> 2);
initialCapacity |= (initialCapacity >>> 4);
initialCapacity |= (initialCapacity >>> 8);
initialCapacity |= (initialCapacity >>> 16);
initialCapacity++;

if (initialCapacity < 0) // Too many elements, must back off
initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements
}
elements = (E[]) new Object[initialCapacity];
}

add

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
   public void addFirst(E e) {
if (e == null)
throw new NullPointerException();
//因为elements.length是2的幂次,(head - 1) & (elements.length - 1)相当于求模操作
//head-1只可能为-1,-1&(elements.length - 1)=(elements.length - 1)
elements[head = (head - 1) & (elements.length - 1)] = e;
if (head == tail)
doubleCapacity();
}


public void addLast(E e) {
if (e == null)
throw new NullPointerException();
elements[tail] = e;
//与head操作类似,为了处理tail=length-1的情况
if ( (tail = (tail + 1) & (elements.length - 1)) == head)
doubleCapacity();
}

public boolean add(E e) {
addLast(e);
return true;
}

public boolean offerFirst(E e) {
addFirst(e);
return true;
}

public boolean offerLast(E e) {
addLast(e);
return true;
}

remove

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
   public E removeFirst() {
E x = pollFirst();
if (x == null)
throw new NoSuchElementException();
return x;
}

public E removeLast() {
E x = pollLast();
if (x == null)
throw new NoSuchElementException();
return x;
}

public E pollFirst() {
int h = head;
@SuppressWarnings("unchecked")
E result = (E) elements[h];
// Element is null if deque empty
if (result == null)
return null;
//删除头元素
elements[h] = null; // Must null out slot
//更改head下标,处理越界情况让head=0
head = (h + 1) & (elements.length - 1);
return result;
}

public E pollLast() {
//获取tail下标,处理tail=0的特殊情况,移除元素后tail=elements.length-1
int t = (tail - 1) & (elements.length - 1);
@SuppressWarnings("unchecked")
E result = (E) elements[t];
if (result == null)
return null;
elements[t] = null;
//tail指向下个要添加元素的索引
tail = t;
return result;
}

删除指定元素

需要遍历数组,时间复杂度较高

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
   public boolean removeFirstOccurrence(Object o) {
if (o == null)
return false;
int mask = elements.length - 1;
int i = head;
Object x;
while ( (x = elements[i]) != null) {
if (o.equals(x)) {
delete(i);
return true;
}
i = (i + 1) & mask;
}
return false;
}

public boolean removeLastOccurrence(Object o) {
if (o == null)
return false;
int mask = elements.length - 1;
//末尾元素的索引
int i = (tail - 1) & mask;
Object x;
while ( (x = elements[i]) != null) {
if (o.equals(x)) {
delete(i);
return true;
}
//从尾到头遍历
i = (i - 1) & mask;
}
return false;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
   private boolean delete(int i) {
//检查有效性
checkInvariants();
final Object[] elements = this.elements;
final int mask = elements.length - 1;
final int h = head;
final int t = tail;
//i前面的元素
final int front = (i - h) & mask;
//i后面的元素
final int back = (t - i) & mask;

//i不在t和h之间
if (front >= ((t - h) & mask))
throw new ConcurrentModificationException();

// Optimize for least element motion
if (front < back) {
// head X X i X X X X tail,head-i的元素大于i-tail元素个数
if (h <= i) {
System.arraycopy(elements, h, elements, h + 1, front);
} else { // Wrap around
// i X X X X tail X X head
System.arraycopy(elements, 0, elements, 1, i);
elements[0] = elements[mask];
System.arraycopy(elements, h, elements, h + 1, mask - h);
}
elements[h] = null;
head = (h + 1) & mask;
return false;
} else {
// head X X X X i X X tail
if (i < t) { // Copy the null tail as well
System.arraycopy(elements, i + 1, elements, i, back);
tail = t - 1;
} else { // Wrap around
// tail X X head X X X X i
System.arraycopy(elements, i + 1, elements, i, mask - i);
elements[mask] = elements[0];
System.arraycopy(elements, 1, elements, 0, t);
tail = (t - 1) & mask;
}
return true;
}
1
2
3
4
5
6
7
8
9
10
   private void checkInvariants() {
//tail没有元素
assert elements[tail] == null;
//head和tail位置重合,队列为空,否则head、tail-1位置有元素
assert head == tail ? elements[head] == null :
(elements[head] != null &&
elements[(tail - 1) & (elements.length - 1)] != null);
//head-1的位置没有元素。
assert elements[(head - 1) & (elements.length - 1)] == null;
}

扩容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
   private void doubleCapacity() {
//tail和head重合的时候进行扩容,此时tail在head左侧
assert head == tail;
int p = head;
int n = elements.length;
int r = n - p; // number of elements to the right of p
int newCapacity = n << 1;
if (newCapacity < 0)
throw new IllegalStateException("Sorry, deque too big");
Object[] a = new Object[newCapacity];
//先复制head到element数组末尾的元素
System.arraycopy(elements, p, a, 0, r);
//在复制0到tail之间的元素
System.arraycopy(elements, 0, a, r, p);
elements = a;
head = 0;
tail = n;
}

java源码解读--hashmap

发表于 2018-05-31 | 分类于 java |
字数统计: | 阅读时长 ≈

HashMap

数据结构特点

其类定义如下

1
2
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable

继承了AbstractMap,实现了Map、Cloneable、Serializable,注意并没有实现Collection接口。
HashMap允许K/V值为null,非线程安全,不保证有序。

1.7版本与1.8版本区别

JDK1.7的hashmp数据结构为数组+链表,而JDK1.8的数据结构为数组+链表+红黑树,加入红黑树是为了解决发生hash碰撞后链表过长从而导致索引效率问题,主要利用了红黑树快速增删改查的特点,时间复杂度从O(n)降为O(logn)。
链表转红黑树条件:链表长度>8时,将链表转化为红黑树。

  • 无冲突:存放在数组
  • 有冲突但链表长度<8:存放在单链表
  • 有冲突但链表长度>8:转化为红黑树并存放

构造方法

4个构造方法,可指定初始容量、负载因子以及输入一个map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
   public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
// 设置扩容阈值,详见tableSizeFor函数
this.threshold = tableSizeFor(initialCapacity);
}


public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}

public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}

public HashMap(Map<? extends K, ? extends V> m) {
this.loadFactor = DEFAULT_LOAD_FACTOR;
putMapEntries(m, false);
}
//将传入的容量大小转化为:大于传入容量的最小的2次幂
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}

初始化参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//默认初始桶大小为16,必须是2的幂次
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
//最大桶大小,2的30次方
static final int MAXIMUM_CAPACITY = 1 << 30;
//负载因子,在容量自动增加前可达到最大值的一种尺度
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//与红黑树有关的参数
//桶的树化阈值:链表最大长度,超过即转化为红黑树
static final int TREEIFY_THRESHOLD = 8;
//桶的链表还原阈值:当低于该值,树转化为链表
static final int UNTREEIFY_THRESHOLD = 6;
//最小树型化容量阈值,当哈希表的容量>该值时,才允许树形化链表
//否则,若桶内元素太多,则直接扩容,而不是树型化
static final int MIN_TREEIFY_CAPACITY = 64;



//存储数据的Node类型数组
transient Node<K,V>[] table;
//HashMap的大小,即HashMap中存储的键值对数量
transient int size;
//扩容阈值,当table的大小大于等于扩容阈值时,就会扩容哈希表。
//扩容是对哈希表进行resize操作,resize操作一般会增加一倍容量
//扩容阈值=容量*负载因子
int threshold;

Node定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//hashmap对链表节点的定义
static class Node<K,V> implements Map.Entry<K,V> {
//hash值
final int hash;
//Key值
final K key;
//Value值
V value;
//下一个node指针
Node<K,V> next;

Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}

public final K getKey() { return key; }
public final V getValue() { return value; }
public final String toString() { return key + "=" + value; }

public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}

public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}

public final boolean equals(Object o) {
if (o == this)
return true;
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}

TreeNode定义

继承了LinkedHashMap的Entry类,有左右指针和前后指针以及颜色属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
//父节点
TreeNode<K,V> parent; // red-black tree links
//左儿子
TreeNode<K,V> left;
//右儿子
TreeNode<K,V> right;
//前一个元素
TreeNode<K,V> prev; // needed to unlink next upon deletion
//颜色
boolean red;

....
}

put方法

put方法主体函数

1
2
3
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}

hash函数

无论1.7还是1.8版本都对hashcode进行了扰动处理,这样做是为了加大哈希码的随机性使分布更均匀,减少hash冲突

1
2
3
4
5
6
7
8
//将key转化为hashcode,调用了一次hashCode + 1次位运算 + 1次异或运算
//需要注意的是:当key=null时,hash值等于0,所以hashmap的key可以为null
//而hashtable在对key进行hashcode()时,若key为null会抛出异常,所以hashtable的key不能为null
//当key不等于null时,先计算hashcode(),然后对哈希码进行扰动处理:按位异或哈希码
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

计算table存储下标

在1.8中没有专门的函数计算entry在桶中的存储位置,只是通过一句代码来完成,即

1
2
//h即hashcode,length即table的长度
h & (length-1)

putVal操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
   final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;

//数组为空则通过resize方法创建,即第一次调用put函数时使用resize初始化哈希表
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;

// 计算插入存储数组的索引i,根据键值key计算的hash值得到
// 若该索引位置上没有元素,则直接插入,否则代表hash冲突
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);

else {
//该分支代表出现hash冲突场景
Node<K,V> e; K k;
//如果table[i]的元素的key和待插入元素的key一样,则用新值覆盖旧值
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;

//如果table[i]的元素是红黑树,直接插入红黑树
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);

//若是链表
else {
for (int binCount = 0; ; ++binCount) {
//遍历完所有的节点没有发现key相等的节点
if ((e = p.next) == null) {
//在p后插入新节点
p.next = newNode(hash, key, value, null);
//判断是否需要树型化
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
//发现有key相等的节点
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
//更新p指向下一个节点
p = e;
}
}
// 发现key冲突,直接用新value覆盖旧value
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;
}

将链表替换为红黑树

当一个桶中的元素个数超过TREEIFY—THRESHOLD(默认是8),就会将链表转化为红黑树。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//该方法将链表节点替换为红黑树节点
final void treeifyBin(Node<K,V>[] tab, int hash) {
int n, index; Node<K,V> e;
// 如果hash表为空,或者hash表中元素小于进行树型化的阈值,就去扩容
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
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);
//如果尾节点为null,说明p是头节点
if (tl == null)
hd = p;
else {
//设置p的前后节点
p.prev = tl;
tl.next = p;
}
tl = p;
} while ((e = e.next) != null);
//让桶元素指向红黑树的头节点
if ((tab[index] = hd) != null)
//调整红黑树
hd.treeify(tab);
}
}

初始化红黑树

treeifyBin方法只将链表节点替换成了红黑树节点,并没有建立起红黑树的结构,接下来需要调用treeify方法构建红黑树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

final void treeify(Node<K,V>[] tab) {
TreeNode<K,V> root = null;
for (TreeNode<K,V> x = this, next; x != null; x = next) {
next = (TreeNode<K,V>)x.next;
x.left = x.right = null;
//头节点为黑色
if (root == null) {
x.parent = null;
x.red = false;
root = x;
}
else {
K k = x.key;
int h = x.hash;
Class<?> kc = null;
//从根节点开始遍历,调整顺序
for (TreeNode<K,V> p = root;;) {
int dir, ph;
K pk = p.key;
if ((ph = p.hash) > h)
dir = -1;
else if (ph < h)
dir = 1;
//碰撞节点的hash值应该都一样,这里不是很明白为什么需要判断不想等的情况
else if ((kc == null &&
(kc = comparableClassFor(k)) == null) ||
(dir = compareComparables(kc, k, pk)) == 0)
//相等或者不能比较的情况
dir = tieBreakOrder(k, pk);

TreeNode<K,V> xp = p;
if ((p = (dir <= 0) ? p.left : p.right) == null) {
x.parent = xp;
//如果当前节点比x的哈希值大,x就是做孩子,否则x就是右孩子
if (dir <= 0)
xp.left = x;
else
xp.right = x;
//平衡红黑树
root = balanceInsertion(root, x);
break;
}
}
}
}
moveRootToFront(tab, root);
}

插入新节点时保证红黑树特性

这里会根据红黑树特性调整结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
      static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
TreeNode<K,V> x) {
//新增节点默认红色
x.red = true;
//xp父节点,xpp祖父节点,xppl祖父节点左节点,xppr祖父节点右节点
for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
//x的父节点为空,x是根节点,是黑色
if ((xp = x.parent) == null) {
x.red = false;
return x;
}
else if (!xp.red || (xpp = xp.parent) == null)
//父节点是黑色,祖父节点是空,直接返回
return root;
//父节点是红色,父节点是祖父节点的左节点
if (xp == (xppl = xpp.left)) {
//祖父节点的右节点是红色
if ((xppr = xpp.right) != null && xppr.red) {
//祖父节点右节点设置为黑色
xppr.red = false;
//父节点设置为黑色
xp.red = false;
//祖父节点设置为红色
xpp.red = true;
//把祖父节点设置为当前节点,并继续循环操作
x = xpp;
}
//如果祖父节点的右节点是黑色或者空
else {
//若x是父节点的右节点,则需要进行左旋
if (x == xp.right) {
root = rotateLeft(root, x = xp);
xpp = (xp = x.parent) == null ? null : xp.parent;
}
//经过左旋x为左节点
if (xp != null) {
//父节点设置为黑色
xp.red = false;
//祖父节点不为空
if (xpp != null) {
//祖父节点设置为红色
xpp.red = true;
//右旋操作
root = rotateRight(root, xpp);
}
}
}
}
//父节点为右节点
else {
//祖父节点左节点为红色
if (xppl != null && xppl.red) {
//祖父节点左节点设置为黑色
xppl.red = false;
//父节点为黑色
xp.red = false;
//祖父节点为红色
xpp.red = true;
//循环操作
x = xpp;
}
//祖父节点左节点为黑色
else {
//右旋
if (x == xp.left) {
root = rotateRight(root, x = xp);
xpp = (xp = x.parent) == null ? null : xp.parent;
}
//x变为右节点
if (xp != null) {
xp.red = false;
if (xpp != null) {
xpp.red = true;
//以祖父节点为支点左旋
root = rotateLeft(root, xpp);
}
}
}
}
}
}

扩容操作

以下两种场景需要扩容:1.初始化哈希表 2.当前数组容量过小进行扩容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81

final Node<K,V>[] resize() {
//扩容前数组
Node<K,V>[] oldTab = table;
//扩容前容量
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
newCap = DEFAULT_INITIAL_CAPACITY;
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<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;
}

get操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
   public V get(Object key) {
Node<K,V> e;
//计算hash值,调用getNode查询,如果为空返回null
return (e = getNode(hash(key), key)) == null ? null : e.value;
}


final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
//通过(n - 1) & hash找到下标
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
//如果第一个元素恰好是待查找元素直接返回
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
//如果不是,需要进一步查找碰撞节点
if ((e = first.next) != null) {
if (first instanceof TreeNode)
//从红黑树中查找对应节点
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
//如果不是红黑树,在链表中遍历查找
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
1234…6
小建儿

小建儿

码农小白成长记

54 日志
8 分类
18 标签
RSS
© 2019 小建儿
本站访客数: |
| 博客全站共字