java集合
1.什么是集合?
Java中的集合collection指一个JAVA对象内部持有其他JAVA对象, 且提供对外接口,这样的Java对象称为集合。
集合的特点是接口(Interface)和实现类(Implementation)分离,并且可以使用泛型。
2. 集合接口及其对应的实现类
黑色为集合接口,黑色为对应的实现类
3.集合中元素的常见操作
集合中插入元素: bollean add( E element)
, Map: V put(K key, V value)
读取元素: Iterator, Map: V get(K key)
List: 有序集合,有两种实现类: ArrayList LinkedList
, 有两种元素的访问方法:一是迭代器顺序访问元素, 二是使用整数索引随机访问元素,常用的随机访问方法:
void add(int index, E element)
void remove(int index)
E get(int index)
E set(int index, E element)
对于ArrayList,可以通过get,set方法随机访问,但是却不适于LinkedList, 链表结构可以使用Iterator接口实现快速增、删操作。以下代码来源《JAVA核心技术卷一》
import java.util.List;
import java.util.LinkedList;
import java.util.Iterator;
import java.util.ListIterator;
public class LinkedListTest {
public static void main(String[] args) {
List<String> a = new LinkedList<>();
a.add("Army");
a.add("Carl");
a.add("Erica");
List<String> b = new LinkedList<>();
b.add("Bob");
b.add("KD");
b.add("Frances");
b.add("Gloria");
// merge string from b to a
ListIterator<String> aIter = a.listIterator();
Iterator<String> bIter = b.iterator();
while(bIter.hasNext()){
if(aIter.hasNext()) aIter.next();
aIter.add(bIter.next());
}
System.out.println(a);
System.out.println(b);
//remove every second word from b
bIter =b.iterator();
while(bIter.hasNext()) {
bIter.next();//skip one element
if(bIter.hasNext()) {
bIter.next();//skip next element
bIter.remove();//remove that element
}
}
System.out.println(b);
// bulk operation:remove all words in b from a
a.removeAll(b);
System.out.println(a);
}
}
散列集合
LinkedList
ArrayList
可以按照一定的顺序给元素排序,如果需要查看某个元素,但是又忘记了位置,则需要遍历所有元素。当集合中元素数量很大的时候,程序执行效率就会很低。针对不太在意元素顺序的情况,Java集合提供了set
接口,对应的实现类主要有: HashSet
TreeSet
- HashSet
在介绍HashSet之前有必要介绍一种常见的数据结构:Hashtable
- Hashtable继承自Dictionary, 实现的接口有:
Map<k,v>
Cloneable
java.io.Serializable
,所以Hashtable保存的是key-value键值对,且均不能为null. Hashtable函数均为同步的,所以线程安全
public class Hashtable<K,V) extends Dictionary<K,V> implememts Map<K,V>, Cloneable, java.io.Serializable{
...
}
- Hashtable通过”拉链法“实现哈希表,包含的成员变量:
table count threshold loadFactor modCount
- table是一个Entry[]数组,Entry表示单向链表。哈希表的”Key-Value" 存储在Entry数组
- count表示Hashtable保存的键值对的数量
- Threshlod是Hashtable的阈值,判断是否需要调整Hashtable的容量。Threshold = Capacity*LoadFactor
- LoadFactor 加载因子
- modCount用来实现fail-fast机制
- Hashtable数据节点Entry的数据结构
private static class Entry<K,V> implements Map.Entry<K,V>{
// 哈希值
int hash;
K key;
V value;
//指向下一个Entry,即链表的下一个节点
Entry<K,V> next;
//构造函数
protected Entry(int hash, K key, V value, Entry<K,V> next){
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
protected Object clone(){
return new Entry<K,V> (hash, key, value, (next == null ?null: (Entry<K,V>) next.clone()));
}
public K getKey(){
retrun key;
}
public V value(){
return value;
}
//设置value, 若value是null,则抛出异常
public V setValue(V value) {
if(value ==null){
throw new NullPointerException();
V oldValue = this.value;
this.calue = value;
return oldValue;
}
// 覆写equals()方法, 判断两个Entry是否相等
//若两个Entry的key value都相等,则认为他们相等
public boolean equals(Object o){
if( !(o instanceof Map.Entry)){
return false;
Map.Entry e= (Map.Entry) 0;
return (key == null ? e.getkey() == null :key.equals(e.getKey())) && (value ==null ? e.getValue==null : value.equals(e.getValue()));
}
public int hashCode(){
return hash ^ (value == null ? 0 : value.hashCode());
}
public String toString(){
return key.toString() + "=" +value.toString();
}
}
- Hashtable构造函数
//默认构造函数
public Hashtable(){
//默认容量为11,加载因子0.75
this(11,0.75f);
}
//制定容量大小
public Hashtable(int initialCapacity){
this(initialCapacity,0.75f);
}
//指定容量大小和加载因子
public Hashtable(int initialCapacity, float loadFactor){
if(initialCapacity < 0){
throw new IllegalArgumentException("IllegalCapacity: " + initialCapacity);
}
if(loadFactor <=0 || Float.isNaN(loadFactor)){
throw new IllegalArgumentException("IllegalLaod: " +loadFactor);
if(initialCapacity ==0)
initialCapacity = 1;
this.loadFactor = loadFactor;
table = new Entry[initialCapacity];
threshold = (int) (initialCapacity * loadFacotr);
}
//包含子MAP的构造器
public Hashtable(Map<? extends K, ? extends V> t){
this(math.max(2*t.size(), 11), 0.75f);
//将子Map全部元素添加到Hashtable
putAlll(t);
}
- Hashtable常用的API
- boolean contains(Object value)
- boolean containsKey(Object key)
- boolean containValue(Object value)
- boolean equals(Object object)
- Enumeration < V > elements()
- Set<Entry<K, V> > entrySet()
- V get(Object key)
- int hashCode()
- boolean isEmpty()
- Set < K> keySet()
- Emumeration < K> keys()
- V put(K key, V value)
- void putAll(Map< ? extends K, ? extends V> map)
- V remove(Object key)
- int size();
- String toString()
- Collection < V > values()
// containsKey()源码 public synchronized boolean containsKey(Object key){ Entry tab[] = table; int hash = key.hashCode(); //计算索引值 // %tab.length防止数据越界 int index = (hash & 0x7FFFFFFF) %tab.length; // 找到key对应的Entry,然后在链表中找出哈希值和键值均相等的元素 for( Entry<K, V> entry = tab[index]; entry!= null; entry= entry.next){ if((entry.hash==hash) && e.key.equal(key){ retrun true; } } return false; }
//elements()作用是返回所有value的枚举对象 public synchronized Enumeration<V> elements(){ return this.<V>getEnumeration(VALUES); } //获取Hashtable枚举类对象 private <T> Enumeration<T> getEnumeration (int type){ if(count ==0){ return (Enumeration<T> emptyEnumerator; }else{ return new Enumerator<T> (type, false); } private static Enumeration emptyEnumerator = new EmptyEnumerator(); //空枚举类 //当Hashtable实际大小为0, 通过Enumeration遍历Hashtable, 返回空枚举类对象 private static class EmptyEnumerator implements Enumeration<Object> { EmptyEnumerator(){ } public boolean hasMoreElements(){ return false; }
//Enumerator接口 private class Enumerator<T> implements Enumeration<T>, Iterator<T> { //指向Hashtable的table Entry[] table = Hashtable.this.table; //hashtable的大小 int index = table.length; Entry<K,V> entry = null; Entry<K,V> lastReturned = null; int type; // iterator为ture, Enumerator 为迭代器,反之位Enumeration 枚举类 boolean iterator; // 将enumerator用作迭代器时, 用来实现Fail-fast机制 protected int exceptedModCount = modCount; Enumerator(int type, boolean iterator){ this.type= type; this.iterator = iterator; } //遍历Table数据从末尾,直到找到不为Null的Entry public boolean hasMoreElements(){ Entry<K,V> e =entry; int i = index; Entry[] t = table; while(e ==null && i>0){ e = t[--i]; } entry = e; index = i; return e!=null; } //获取下一个元素 public T nextElements(){ Entry<K,V> et = entry; int i = index; Entry[] t = table; while(et==null && i>){ et = t[--i[; } entry = rt; index =i; if(et ! = null){ Entry<K,V> e = lastReturned = entry; entry = e.next; return type == KEYS ? (T)e.key : (type == VALUES ? (T) e.value : (T) e); } throw new NoSuchElementsException("HashtableEnumrator"); }
//put()源码 public synchronized V put( K key, V value){ //不能存放null 元素 if( value ==null){ throw new NullPointerException(); } // 若Hashtable已存在键位key的键值对,用新value替换旧value Entry tab[] = table; int hash = key.hashCode(); int index = (hash &0x7FFFFFFF) % tab.length; for(Entry<K,V> e = tab[index]; e !=null; e= e.next){ if((e.hash == hash) && e.key.equals(key)){ V old = e.value; e.value = value; return old; } } //若Hashtable不存在键为key的键值对 //1. 将修改统计数+1 modCount++; ///2.若Hashtable实际容量 > 阈值, 则调整Hashtable大小 if( count >= threshold){ rehash(); tab = table; index = (hash & 0x7FFFFFFF) % tab.length; } // 3. 将Hashtable中Index位置的Entry保存到e Entry<K,V> e = tab[index]; // 4. 创建新的Etry节点,并将新Entry插入Hashtable的index位置, 并设置e为新的Entry的下一个元素,即新entry为链表表头 tab[index] = new Entry<K,V> (hash, key , value, e); //5. Hashtable容量+1 count++; return all; }
- Hashtable的遍历
- 1 遍历Hashtable 键值对
// 1. 根据entrySet()获取Hashtable的键值对的集合 Integer integ = null; Iterator iter = table.entrySet().iterator(); // 通过Iterator迭代器遍历第一步得到的集合 while(iter.hasNext()){ Map.Entry entry = (Map.Entry) iter.next();; //获取key key = (String)enry.getKey(); integ = (Integer) entry.getValue(); }
- 2 通过Iteraotr遍历Hashtable键
String key =null; Integer iteg = null; Iterator iter = table.keySet().iterator(); while(iter.hasNext()){ key = (String) iter.next(); integ = (Integer) table.get(key); }
- 3 通过Iterator遍历Hashtable的值
Integer value = null; Collection c= table.values(); Iterator iter = table.iterator(); while( iter.hasNext()){ value = (Integer)iter.next(); }
- 4 通过Enumeration遍历Hashtable的键
Enumeration emu = table.keys(); while(emu.hasMoreElements()){ System,out,println(emu.nextElements()); }
- 5 通过Enumeration遍历Hashtable的值
Enumeration emu = table.elements(); while(emu.hasMoreElements()){ System.out.printn(emu.nextElements()); }
- Hashtable遍历的测试实例
import java.util.*;
import java.util.Hashtable;
public class Hash_Table_Test {
public static void main(String[] args) {
int val =0;
String key = null;
Random r = new Random();
Integer value = null;
Hashtable<String, Integer> table = new Hashtable<>();
for(int i =0; i<12; i++) {
val = r.nextInt(100);
key =String.valueOf(val);
value = r.nextInt(5);
table.put(key,value);
System.out.println("Key:" +key +"value:" +value);
}
//通过entrySet()遍历
iteratorHashtableByEntryset(table);
//通过keySet()遍历Hashtable的key-value
iteratorHashtableByKeyset(table);
//通过Enumeration 遍历Hashtable 的key
enumHashtableKey(table);
//通过Enumeration遍历Hashtable 的value
enumHashtableValue(table);
//遍历Hashtable的value
iteratorHashtableJustValue(table);
}
private static void iteratorHashtableByEntryset(Hashtable table) {
if(table==null) {
return ;
}
System.out.println("\niterator Hashtable by entrySet");
String key = null;
Integer value = null;
Iterator iter = table.entrySet().iterator();
while(iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
key = (String)entry.getKey();
value = (Integer)entry.getValue();
System.out.println(key + "-" + value.intValue());
}
}
private static void iteratorHashtableByKeyset(Hashtable table) {
if (table == null)
return ;
System.out.println("\niterator Hashtable by keySet");
String key =null;
Integer value = null;
Iterator iter = table.keySet().iterator();
while(iter.hasNext()) {
key = (String)iter.next();
value =(Integer) ((Map) table).get(key);
System.out.println(key + "--" + value.intValue());
}
}
private static void enumHashtableKey(Hashtable table) {
if (table == null) {
return ;
}
System.out.println("\nenumHashtableKey");
Enumeration emu =table.keys();
while(emu.hasMoreElements()) {
System.out.println(emu.nextElement());
}
}
private static void enumHashtableValue(Hashtable table) {
if(table == null)
return ;
System.out.println("\nenumHashtableValue");
Enumeration emu =table.elements();
while(emu.hasMoreElements()) {
System.out.println(emu.nextElement());
}
}
private static void iteratorHashtableJustValue(Hashtable table) {
Collection c = table.values();
Iterator iter = c.iterator();
while(iter.hasNext()) {
System.out.println(iter.next());
}
}
}
Hashtable是线程安全的, 相对而言,HashMap更为常见, HashSet的底层实现是HashMap, HashMap与Hashtable相似,下面做一简单介绍
- HashMap
- 初识HashMap
HashMap存储的内容也是键值对
HashMap extends AbstractMap, implements Map, Cloneable, java.io.Serialization
HashMap非同步,线程不安全
Key, value均可以为null - HashMap构造函数
HashMap构造函数同Hashtable, 也有initialCapacity和loadFactor - HashMap本质是一个链表数组,主要包含:
- table , 链表数组Entry[], Entry为单向链表, 有next指向下一个Entry, int hash, key-value
- table , 链表数组Entry[], Entry为单向链表, 有next指向下一个Entry, int hash, key-value
- HashMap源码分析
package java.util; import java.io.*; public class HashMap<K,V> extends AbstraactMap<K,V>, implements Map, Cloneable, Serializable{ // 默认初始容量为16,必须为2的幂 static final int DEFAULT_INITIAL_CAPACITY = 16; //最大容量必须是2的幂,且小于2的30次方,传入容量过大会被这个值替换 static final int MAXIMOM_CAPACITY = 1<<30; static final float DEFAULT_LOAD_FACTOR = 0.75f; //存储Entry的数组,长度是2的幂 transient Entry[] table; //HashMap的大小,指存储键值对的数目 transient int size; //HashMap阈值,用于判断是否需要调整容量 Threshold =Capacity*LoadFactor int threshold; final float loadFactor; // HashMao被改变的次数 transient volatie int modCount; //获取key对应的value public V get(Object key){ if(key==null){ return getForNullKey(); } //获取key的hash值 int hash = hash(key.hashCode()); //在该hash值对应的链表查找键值等于key的元素 for(Entry<K,V> e = table[indexFor(hash, table.length)]; e!=null; e = e.next()){ Object k; if(e.hash == hash&& ( (k = e.key) == key || key.equals(k))){ return e.value; } return null; } private V getForNullKey(){ for(Entry<K,V> e = table[0]; e!= null; e= e.e=next){ if(e.key = null; return e.value; } return null; } //返回键为key的键值对 final Entry<K,V> getEntry(Object key){ int hash = (key==null ? 0: hash(key.hashCode()); for(Entry<K,V> e = table[indexFor(hash,table.length)]; e!= null; e = e.next)} Object k; if(e.hash == hash && ((key=e.key) == key || (key != null&& key.equals(k)))){ return e; } return null; } //containsKey() public boolean containsKey(Object key){ return getEntry(key) != null; } //所以containsKey()首先通过getEntry()获取key对应的Entry,然后判读该Entry是否为Null final Entry<K,V> getEntry(Object key){ int hash = (key==null) ? 0: hash(key.hashCode()); for(Entry<K,V> e = table[indexFor(hash, table.length)]; e!= null; e.next){ Object k; if(e.hash = hash && ( (k = e.key) ==key || ( key != null && key.equals(k)))) return e; } return null; } //containsValue() public boolean containsValue(Object value){ if(value ==null){ return containsNullValue(); } Entry[] tab = table; for(int i=0; i< tab.length; i++){ for(Entry e = tab[i]; e!= null; e= e.next){ if(value.equals(e.value){ return true; } } return false; } // entrySet, values(), keySet() public Set<Map.Entry<K,V>> entrySet(){ return entrySet0(); } private Set<Map.Entry<K,V>> entrySet0(){ set<Map.Entry<K,V>> es = entrySet; return es != null ? es :(entrySet = new EntrySet()); } private final class entrySet extends AbstractSet<Map.Entry<K,V>> { public Iterator<Map.Entry<K,V>> iterator(){ return newEntryIterator(); } public boolean contains(Object o){ if( o!= instanceof Map.Entry)){ return false; } Map.Entry<K,V> candidate = getEntry(e.getKey()); return candidate ! = null && candidate.equals(e); }
- 初识HashMap
- HashSet
- 内部采用HashMap存储数据。 JDK 7采用数组+链表的形式,每一个链表为一个桶(Bucket). put元素时,首先计算该对象的HashCode,然后与bucket 总数取余, 即为对象在哈希表中的索引。当桶被占满时,就会发生hash collision, 这时链表长度很长,新插入对象又需要遍历桶中所有元素,程序执行耗时增加。为此,JDK 8引入数组+链表+红黑树 实现。当桶中对象数量超过8个,链表会自动转变为
红黑树。
注意这里的桶表示每一个Entry链表的数目
HashCode可以是任意整数,包括正数或负数 - 元素桶位置索引的计算:
// 扰动计算: 将元素的哈希值h与h的无符号右移16位进行异或运算,得到一个新的hash值 static final int hash(Object key){ int h; return (key== null) ? 0 : (h = key.hashCode()) ^ (h>>>16); }
- HashSet特点 - 1. 底层采用HashMap实现 - 2. 元素不可重复 - 3. 排列无序 - 4. 存取速度快,但是线程不安全
- 内部采用HashMap存储数据。 JDK 7采用数组+链表的形式,每一个链表为一个桶(Bucket). put元素时,首先计算该对象的HashCode,然后与bucket 总数取余, 即为对象在哈希表中的索引。当桶被占满时,就会发生hash collision, 这时链表长度很长,新插入对象又需要遍历桶中所有元素,程序执行耗时增加。为此,JDK 8引入数组+链表+红黑树 实现。当桶中对象数量超过8个,链表会自动转变为
- TreeSet
TreeSet与HashSet很类似, 树集是有序集合,元素以一定的顺序添加到集合中,在元素遍历时,迭代器以排列好的顺序访问每个元素。HashMap对键进行散列,TreeMap根据键的整体顺序对元素进行排序。与键关联的值不能进行散列或比较
TreeSet跟HashSet一样,底层实现为TreeMap.
- TreeMap介绍
- TreeMap是有序映射,保存键值对,底层通过红黑树实现
extends AbstractMap
implements NavigableMap<K,V> ,Cloneable, Serializable
- TreeMap基于Red-Black tree实现,根据其键的自然顺序排序,或者根据创建映射时提供的Comparator进行排序,具体取决于使用时的构造方法
- TreeMap本质是红黑树,包含
root size comparator
,root 是红黑树的根节点, Entry类型,包含6个基本组成成分: key、value、left、right、parent、color。Entry节点根据key进行排序,Entry节点包含的内容为value。size是红黑树的节点数。 - TreeMap的红黑树相关内容
- 1 数据结构
- 1.1 红黑树的节点颜色–红色
private static final boolean RED = false
- 1.2 红黑树的节点颜色–黑色
private static final boolean BLACK = true
- 1.3 红黑树节点对应的类
static final class Entry<K,V> implements Map.Entry<K,V> {...}
节点根据key进行排序,包含的内容为value.
- 1.1 红黑树的节点颜色–红色
- 2 相关操作
- 2.1 左旋
private void rotateLeft(Entry<K,V> p) { ... }
- 2.2 右旋
private void rotateRight(Entry<K,V> p) {...}
- 2.3 插入操作
public V put(K key, V value)
- 2.4 插入修正操作
private void fixAfterInsertion(Entry<K,V> x)
- 2.5 删除操作
private void deleteEntry(Entry<K,V> p) {...}
- 2.6 删除修正操作,保证红黑树删除之后仍为一颗红黑树
private void fixAfterDeletion(Entry<K,V> x) { .. }
- 2.1 左旋
- 3 TreeMap构造函数
- 3.1 默认构造函数
使用java默认的比较器比较Key大小,从而对其进行排序
pubic TreeMap(){ ... }
- 3.2 带比较器的构造器
public TreeMap(Comparator<? super K> comparator) { this.comparator = comprarator; }
- 3.3 带Map的构造器,Map会成为TreeMap的子集
public TreeMap(Map<? extends K, ? extends V> m) { comparator = null; putAll(m); }
putAll将m中的所有元素添加到TreeMap中,putAll源码:
public TreeMap(Map< ? extends K, ? extends V> m) { for(Map.Entry<? extends K, ? extends V> e: m.entrySet()){ put(e.getKey(), e.getValue()); } }
- 3.4 带sortedMap构造器
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){ } }
- 3.1 默认构造函数
- 4 TreeMap的Entry相关函数
TreeMap Entry相关函数有:firstEntry()
lastEntry()
lowerEntry()
higherEntry()
floorEntry()
ceilingEntry()
pollFirstEntry()
pollLastEntry()
//firstEntry()源码 public Map.Entry<K,V> firstEntry(){ return exportEntry(getFirstEntry()); } final Entry<K,V> getFirstEntry(){ Entry<K,V> p = root; if(p != null){ while( p.left != null) { p = p.lleft; } return p; } }
- 1 数据结构
- 5 TreeMap的key相关函数
firstKey() lastKey() lowerKey() higherKey() floorKey() ceilingKey()
//ceilingKey()源码, 返回大于等于key的最小的键值对所对应的KEY,没有的话返回Null public K ceilingKey( K key){ return keyOrNull(getCeilingEntry(key)); } static <K,V> K keyOrNull(TreeMap.Entry<K,V> E){ return e == null ? null: e.key; } // getCeilingKey(K key) 作用是获取TreeMap中大于/等于Key的最小节点,若不存在就返回Null
- TreeMap的遍历
- 1 遍历TreeMap的键值对
//假设map是TreeMap对象 Integer value = null; Iterator iter = map.entrySet().iterator(); while(iter.hasNext()){ Map.Entry<K,V> entry = (Map.Entry<K,V>) iter.next(); key = (String) entry.getKey(); value = (Integer) entry.getValue(); }
- TreeSet介绍
public class TreeSet<E>
extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, Serializable
源码解析
public class TreeSet<E> extends AbstractSet<E> implements Navigable<E>, Cloneable, java.io.Serializable {
// NavigableMap对象
private transient NavigableMap<E, Object> m;
// TreeSet是通过TreeMap实现的
// PRESENT是键值对中的值
private static final Object PRESENT = new Object();
//返回将元素按照升序排列的迭代器
public Iterator<E> iterator() {
return m.navigableKeySet().iterator();
}
//返回按照降序排列
public Iterator<E> descendingIterator() {
return m.descendingKeySet().iterator();
}
- TreeSet的遍历
//1 Iterator顺序遍历
for(Iterator iter = set.iterator(); iter.hasNext(); ) {
iter.next();
}
//2 j降序遍历
for(Iterator iter = set.descendingIterator(); iter.hasNext(); ){
iter.next();
}
//3 for-each遍历
String[] arr = (String[]) set.toArray(new String[0]);
for(String str :arr) {
System.out.println("for each : %s\n", str);
}
用HashSet/HashMap遍历元素时,无法保证顺序。
这时就需要LinkedHashSet/LinkedHashMap,虽然以牺牲空间、时间为代价,但是可以通过维护运行于所有条目的双向链表,保证元素的迭代顺序。这顺序可以为插入顺序或访问顺序
- LinkedHashMap
public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>
LinkedHasnMap是HashMap子类,HashMap的所有非private方法均可以使用。由于是双向链表,有额外的属性:
// The head of the doubly linked list
private transient Entry<K,V> header;
// the iterator ordering method ,true表示access order 访问顺序,false表示插入顺序
private final boolean accessOrder;
数据结构上,LinkedList有两个特殊的属性: Entry<K,V> before/after, 用于维护Entry插入的先后顺序, 注意与Entry<K,V> next的区别, next维护HashMap指定table上连接的Entry顺序;双向链表的头部存放最久访问或最先插入的节点,尾部为最近访问或最近插入的节点,迭代器遍历是从链表头部开始到链表尾部结束,链表头部有个空Header节点,该节点不存放键值对,是LinkedHashMap类的成员属性,循环双向列表的入口。
LinkedHashMap初始化调用的是HashMap的初始化方法,重写了init()
方法:
void init() {
header = new Entry<K,V> (-1,null,null,null); //依次为HashCode, key, value, next
header.before = header.after = header; //维持双向循环列表
==以上代码中的header,是用来表示开始和结束元素,目的是记录第一个插入元素,方便遍历时找到第一个元素。
- LinkedHashMap元素的存储
LinkedHashMap并未重写整个put方法,而是重写了put方法调用的子方法,void recordAccess(HashMap m) void addEntry(int hash, K key, V value, int bucketIndex) void creadEntry()
提供了特有的双向链表的实现
put
public V put(K key, V value) {
if( key == null) {
return putForNullKey(value);
//j计算key的hash之,进而确定在数组Table中的位置
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
//查看桶中是否有相同的key, 如果没有,直接插入,如果有,替换旧值,并返回旧值。
for(Entry<K,V> e = table[i]; e!= null; e= e.next) {
Object k;
if( e.hash==hash && (( k = e.key) ==key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordValue(this);
return oldValue;
}
}
modCount++;
//多态,重写了addEntry方法
addEntry(hash, key, value, i);
return null;
}
void addEntry(int hash, K key, V value, int bucketIndex){
//调用creat方法,将新元素以双向链表形式加入映射
creatEntry(hash, key ,value, bucketIndex);
// remove eldest entry if instructed, else grow capacity if appreciate
Entry<K,V> eldest = header.after;
if(removeEldestEntry(eldest)){
removeEntryForKey(eldest.key);
}else {
if(size >= threshold){
resize(2* table.length);
}
}
void creatEntry(int hash, K key, V value, int buketIndex){
HashMap.Entry<K,V> old = table[bucketIndex];
Entry<K,V> e= new Entry<K,V> (hash, key, value, old);
table[bucketIndex] = e;
//将该节点插入表表尾部, 让新Entry和原链表生成一个双向链表
e.addBefore(header);
size++;
}
private void addBefore(Entry<K,V> existingEntry){
after = existingEntry;
before = existingEntry.before;
before. after = this.
after.before = this;
}
所以,creatEntry方法并不会拓展table大小,该方法保留table中bucketIndex处的节点,调用Entry构造方法添加一个节点,即将当前节点的Next引用指向Table[bucketIndex]的节点,之后调用e.addBefore(header)修改链表,将e节点添加到header节点之前
总之, LinkedHashMap实现就是HashMap+LinkedList方式,以HashMap维护护具结构, 以LinkedList维护插入顺序