HashMap研究

一篇文章彻底搞懂哈希表 Table of Contents 1.原理讲解 1.1散列表的概述 1.2散列表的优缺点 1.3散列表的关键问题之散列函数的构建 1.4散列表的关键问题之散列冲突的处理 1.5散列表的性能分析 2.源码实现 2.1 节点定义 2.2 迭代器​​​​​ 2.3 数据结构 3.内存管理 3.1空间配置器 3.2 构造函数 3.3插入函数insert 3.4元素地... 阅读详情

hashTable和hashMap的不同

Hashtable继承自Dictionary类,而HashMap是Java1.2引进的Map interface的一个实现
 
HashMap允许将null作为一个entry的key或者value,而Hashtable不允许
 
还有就是,HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为contains方法容易让人引起误解。

最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在
多个线程访问Hashtable时,不需要自己为它的方法实现同步,而HashMap
就必须为之提供外同步。
 
Hashtable和HashMap采用的hash/rehash算法都大概一样,所以性能不会有很大的差异。

HashTable的应用非常广泛,HashMap是新框架中用来代替HashTable的类,也就是说建议使用HashMap,不要使用HashTable。可能你觉得HashTable很好用,为什么不用呢?这里简单分析他们的区别。

1.HashTable的方法是同步的,HashMap未经同步,所以在多线程场合要手动同步HashMap这个区别就像Vector和ArrayList一样。

2.HashTable不允许null值(key和value都不可以),HashMap允许null值(key和value都可以)。

3.HashTable有一个contains(Object value),功能和containsValue(Object value)功能一样。

4.HashTable使用Enumeration,HashMap使用Iterator。

以上只是表面的不同,它们的实现也有很大的不同。

5.HashTable中hash数组默认大小是11,增加的方式是 old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数。

6.哈希值的使用不同,HashTable直接使用对象的hashCode,代码是这样的:
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
而HashMap重新计算hash值,而且用与代替求模:
int hash = hash(k);
int i = indexFor(hash, table.length);

static int hash(Object x) {
  int h = x.hashCode();

  h += ~(h << 9);
  h ^= (h >>> 14);
  h += (h << 4);
  h ^= (h >>> 10);
  return h;
}
static int indexFor(int h, int length) {
  return h & (length-1);
}
以上只是一些比较突出的区别,当然他们的实现上还是有很多不同的,比如
HashMap对null的操作。

1.HashTable的方法是同步的,HashMap未经同步,所以在多线程场合要手动同步HashMap这个区别就像Vector和ArrayList一样。

2.HashTable不允许null值(key和value都不可以),HashMap允许null值(key和value都可以)。

3.HashTable有一个contains(Object value),功能和containsValue(Object value)功能一样。

4.HashTable使用Enumeration,HashMap使用Iterator。

以上只是表面的不同,它们的实现也有很大的不同。

5.HashTable中hash数组默认大小是11,增加的方式是 old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数。

6.哈希值的使用不同,HashTable直接使用对象的hashCode,代码是这样的:
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
而HashMap重新计算hash值,而且用与代替求模:
int hash = hash(k);
int i = indexFor(hash, table.length);

static int hash(Object x) {
  int h = x.hashCode();

  h += ~(h << 9);
  h ^= (h >>> 14);
  h += (h << 4);
  h ^= (h >>> 10);
  return h;
}
static int indexFor(int h, int length) {
  return h & (length-1);
}
以上只是一些比较突出的区别,当然他们的实现上还是有很多不同的,比如
HashMap对null的操作。

Java Collections---HashMap深度分析与比较

线性表,链表,哈希表是常用的数据结构,在进行Java开发时,JDK已经为我们提供了一系列相应的类来实现基本的数据结构。这些类均在java.util包中。本文试图通过简单的描述,向读者阐述各个类的作用以及如何正确使用这些类。

Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMap

Collection接口
  Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。
  所有实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数用于创建一个空的Collection,有一个Collection参数的构造函数用于创建一个新的Collection,这个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection。
  如何遍历Collection中的每一个元素?不论Collection的实际类型如何,它都支持一个iterator()的方法,该方法返回一个迭代子,使用该迭代子即可逐一访问Collection中每一个元素。典型的用法如下:
    Iterator it = collection.iterator(); // 获得一个迭代子
    while(it.hasNext()) {
      Object obj = it.next(); // 得到下一个元素
    }

  由Collection接口派生的两个接口是List和Set。

List接口
  List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。
和下面要提到的Set不同,List允许有相同的元素。
  除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。
  实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。

LinkedList类
  LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法在LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。
  注意LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:
    List list = Collections.synchronizedList(new LinkedList(...));

ArrayList类
  ArrayList实现了可变大小的数组。它允许所有元素,包括null。ArrayList没有同步。
size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。
  每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。
  和LinkedList一样,ArrayList也是非同步的(unsynchronized)。

Vector类
  Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和ArrayList创建的Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationException,因此必须捕获该异常。

Stack 类
  Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。

Set接口
  Set是一种不包含重复的元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。
  很明显,Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。
  请注意:必须小心操作可变对象(Mutable Object)。如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。

Map接口
  请注意,Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个value。Map接口提供3种集合的视图,Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。

Hashtable类
  Hashtable继承Map接口,实现一个key-value映射的哈希表。任何非空(non-null)的对象都可作为key或者value。
  添加数据使用put(key, value),取出数据使用get(key),这两个基本操作的时间开销为常数。
Hashtable通过initial capacity和load factor两个参数调整性能。通常缺省的load factor 0.75较好地实现了时间和空间的均衡。增大load factor可以节省空间但相应的查找时间将增大,这会影响像get和put这样的操作。
使用Hashtable的简单示例如下,将1,2,3放到Hashtable中,他们的key分别是”one”,”two”,”three”:
    Hashtable numbers = new Hashtable();
    numbers.put(“one”, new Integer(1));
    numbers.put(“two”, new Integer(2));
    numbers.put(“three”, new Integer(3));

  要取出一个数,比如2,用相应的key:
    Integer n = (Integer)numbers.get(“two”);
    System.out.println(“two = ” + n);

  由于作为key的对象将通过计算其散列函数来确定与之对应的value的位置,因此任何作为key的对象都必须实现hashCode和equals方法。hashCode和equals方法继承自根类Object,如果你用自定义的类当作key的话,要相当小心,按照散列函数的定义,如果两个对象相同,即obj1.equals(obj2)=true,则它们的hashCode必须相同,但如果两个对象不同,则它们的hashCode不一定不同,如果两个不同对象的hashCode相同,这种现象称为冲突,冲突会导致操作哈希表的时间开销增大,所以尽量定义好的hashCode()方法,能加快哈希表的操作。
  如果相同的对象有不同的hashCode,对哈希表的操作会出现意想不到的结果(期待的get方法返回null),要避免这种问题,只需要牢记一条:要同时复写equals方法和hashCode方法,而不要只写其中一个。
  Hashtable是同步的。

HashMap类
  HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null value和null key。,但是将HashMap视为Collection时(values()方法可返回Collection),其迭代子操作时间开销和HashMap的容量成比例。因此,如果迭代操作的性能相当重要的话,不要将HashMap的初始化容量设得过高,或者load factor过低。

WeakHashMap类
  WeakHashMap是一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收。

总结
  如果涉及到堆栈,队列等操作,应该考虑用List,对于需要快速插入,删除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList。
  如果程序在单线程环境中,或者访问仅仅在一个线程中进行,考虑非同步的类,其效率较高,如果多个线程可能同时操作一个类,应该使用同步的类。
  要特别注意对哈希表的操作,作为key的对象要正确复写equals和hashCode方法。
  尽量返回接口而非实际的类型,如返回List而非ArrayList,这样如果以后需要将ArrayList换成LinkedList时,客户端代码不用改变。这就是针对抽象编程。

Java中对HashMap的深度分析与比较在Java的世界里,无论类还是各种数据,其结构的处理是整个程序的逻辑以及性能的关键。由于本人接触了一个有关性能与逻辑同时并存的问题,于是就开始研究这方面的问题。找遍了大大小小的论坛,也把《Java 虚拟机规范》,《apress,.java.collections.(2001),.bm.ocr.6.0.shareconnector》,和《Thinking in Java》翻了也找不到很好的答案,于是一气之下把JDK的 src 解压出来研究,扩然开朗,遂写此文,跟大家分享感受和顺便验证我理解还有没有漏洞。 这里就拿HashMap来研究吧。

  HashMap可谓JDK的一大实用工具,把各个Object映射起来,实现了“键--值”对应的快速存取。但实际里面做了些什么呢?

  在这之前,先介绍一下负载因子和容量的属性。大家都知道其实一个 HashMap 的实际容量就 因子*容量,其默认值是 16×0.75=12; 这个很重要,对效率很一定影响!当存入HashMap的对象超过这个容量时,HashMap 就会重新构造存取表。这就是一个大问题,我后面慢慢介绍,反正,如果你已经知道你大概要存放多少个对象,最好设为该实际容量的能接受的数字。

  两个关键的方法,put和get:

  先有这样一个概念,HashMap是声明了 Map,Cloneable, Serializable 接口,和继承了 AbstractMap 类,里面的 Iterator 其实主要都是其内部类HashIterator 和其他几个 iterator 类实现,当然还有一个很重要的继承了Map.Entry 的 Entry 内部类,由于大家都有源代码,大家有兴趣可以看看这部分,我主要想说明的是 Entry 内部类。它包含了hash,value,key 和next 这四个属性,很重要。put的源码如下

public Object put(Object key, Object value) {
Object k = maskNull(key);

  这个就是判断键值是否为空,并不很深奥,其实如果为空,它会返回一个static Object 作为键值,这就是为什么HashMap允许空键值的原因。

int hash = hash(k);
int i = indexFor(hash, table.length);

  这连续的两步就是 HashMap 最牛的地方!研究完我都汗颜了,其中 hash 就是通过 key 这个Object的 hashcode 进行 hash,然后通过 indexFor 获得在Object table的索引值。

  table???不要惊讶,其实HashMap也神不到哪里去,它就是用 table 来放的。最牛的就是用 hash 能正确的返回索引。其中的hash算法,我跟JDK的作者 Doug 联系过,他建议我看看《The art of programing vol3》可恨的是,我之前就一直在找,我都找不到,他这样一提,我就更加急了,可惜口袋空空啊!!!

  不知道大家有没有留意 put 其实是一个有返回的方法,它会把相同键值的 put 覆盖掉并返回旧的值!如下方法彻底说明了 HashMap 的结构,其实就是一个表加上在相应位置的Entry的链表:

for (Entry e = table[i]; e != null; e = e.next) {
 if (e.hash == hash && eq(k, e.key)) {
  Object oldvalue = e.value;
  e.value = value; //把新的值赋予给对应键值。
  e.recordAccess(this); //空方法,留待实现
  return oldvalue; //返回相同键值的对应的旧的值。
 }
}
modCount++; //结构性更改的次数
addEntry(hash, k, value, i); //添加新元素,关键所在!
return null; //没有相同的键值返回
}

  我们把关键的方法拿出来分析:

void addEntry(int hash, Object key, Object value, int bucketIndex) {
table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);

  因为 hash 的算法有可能令不同的键值有相同的hash码并有相同的table索引,如:key=“33”和key=Object g的hash都是-8901334,那它经过indexfor之后的索引一定都为i,这样在new的时候这个Entry的next就会指向这个原本的table[i],再有下一个也如此,形成一个链表,和put的循环对定e.next获得旧的值。到这里,HashMap的结构,大家也十分明白了吧?

if (size++ >= threshold) //这个threshold就是能实际容纳的量
resize(2 * table.length); //超出这个容量就会将Object table重构

  所谓的重构也不神,就是建一个两倍大的table(我在别的论坛上看到有人说是两倍加1,把我骗了),然后再一个个indexfor进去!注意!!这就是效率!!如果你能让你的HashMap不需要重构那么多次,效率会大大提高!

  说到这里也差不多了,get比put简单得多,大家,了解put,get也差不了多少了。对于collections我是认为,它是适合广泛的,当不完全适合特有的,如果大家的程序需要特殊的用途,自己写吧,其实很简单。(作者是这样跟我说的,他还建议我用LinkedHashMap,我看了源码以后发现,LinkHashMap其实就是继承HashMap的,然后override相应的方法,有兴趣的同人,自己looklook)建个 Object table,写相应的算法,就ok啦。

  举个例子吧,像 Vector,list 啊什么的其实都很简单,最多就多了的同步的声明,其实如果要实现像Vector那种,插入,删除不多的,可以用一个Object table来实现,按索引存取,添加等。

  如果插入,删除比较多的,可以建两个Object table,然后每个元素用含有next结构的,一个table存,如果要插入到i,但是i已经有元素,用next连起来,然后size++,并在另一个table记录其位置。
源码分析:HashMap
     

HashMap是Java新Collection Framework中用来代替HashTable的一个实现,HashMap和HashTable的区别是: HashMap是未经同步的,而且允许null值。HashTable继承Dictionary,而且使用了Enumeration,所以被建议不要使用。
HashMap的声明如下:
public class HashMap extends AbstractMap implements Map, Cloneable,Serializable
有关AbstractMap:http://blog.youkuaiyun.com/treeroot/archive/2004/09/20/110343.aspx
有关Map:http://blog.youkuaiyun.com/treeroot/archive/2004/09/20/110331.aspx
有关Cloneable:http://blog.youkuaiyun.com/treeroot/archive/2004/09/07/96936.aspx
这个类比较复杂,这里只是重点分析了几个方法,特别是后面涉及到很多内部类都没有解释
不过都比较简单。

static final int DEFAULT_INITIAL_CAPACITY = 16; 默认初始化大小

static final int MAXIMUM_CAPACITY = 1 << 30; 最大初始化大小

static final float DEFAULT_LOAD_FACTOR = 0.75f; 默认加载因子

transient Entry[] table; 一个Entry类型的数组,数组的长度为2的指数。

transient int size; 映射的个数

int threshold; 下一次扩容时的值

final float loadFactor; 加载因子

transient volatile int modCount; 修改次数

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);
   int capacity = 1;
   while (capacity < initialCapacity)
     capacity <<= 1;
   this.loadFactor = loadFactor;
   threshold = (int)(capacity * loadFactor);
   table = new Entry[capacity];
   init();
}

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

public HashMap() {
   this.loadFactor = DEFAULT_LOAD_FACTOR;
   threshold = (int)(DEFAULT_INITIAL_CAPACITY);
      注意:这里应该是一个失误! 应该是:threshold =(int)(DEFAULT_INITIAL_CAPACITY * loadFactor);
   table = new Entry[DEFAULT_INITIAL_CAPACITY];
   init();
}

public HashMap(Map m) {
   this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY),                                   DEFAULT_LOAD_FACTOR);
   putAllForCreate(m);
}

void init() {}

static final Object NULL_KEY = new Object();

static Object maskNull(Object key){
   return (key == null ? NULL_KEY : key);
}

static Object unmaskNull(Object key) {
   return (key == NULL_KEY ? null : key);
}

static int hash(Object x) {
   int h = x.hashCode();
   h += ~(h << 9);
   h ^= (h >>> 14);
   h += (h << 4);
   h ^= (h >>> 10);
return h;
}
在HashTable中没有这个方法,也就是说HashTable中是直接用对象的hashCode值,但是HashMap做了改进 用这个算法来获得哈希值。

static boolean eq(Object x, Object y) {
   return x == y || x.equals(y);
}

static int indexFor(int h, int length) {
   return h & (length-1);
}
根据哈希值和数组的长度来返回该hash值在数组中的位置,只是简单的与关系。

public int size() {
   return size;
}

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

public Object get(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry e = table[i];
   while (true) {
     if (e == null) return e;
     if (e.hash == hash && eq(k, e.key)) return e.value;
     e = e.next;
   }
}
这个方法是获取数据的方法,首先获得哈希值,这里把null值掩饰了,并且hash值经过函数hash()修正。 然后计算该哈希值在数组中的索引值。如果该索引处的引用为null,表示HashMap中不存在这个映射。 否则的话遍历整个链表,这里找到了就返回,如果没有找到就遍历到链表末尾,返回null。这里的比较是这样的:e.hash==hash && eq(k,e.key) 也就是说如果hash不同就肯定认为不相等,eq就被短路了,只有在 hash相同的情况下才调用equals方法。现在我们该明白Object中说的如果两个对象equals返回true,他们的 hashCode应该相同的道理了吧。假如两个对象调用equals返回true,但是hashCode不一样,那么在HashMap 里就认为他们不相等。

public boolean containsKey(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry e = table[i];
   while (e != null) {
     if (e.hash == hash && eq(k, e.key)) return true;
     e = e.next;
   }
  return false;
}
这个方法比上面的简单,先找到哈希位置,再遍历整个链表,如果找到就返回true。

Entry getEntry(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry e = table[i];
   while (e != null && !(e.hash == hash && eq(k, e.key)))
     e = e.next;
   return e;
}
这个方法根据key值返回Entry节点,也是先获得索引位置,再遍历链表,如果没有找到返回的是null。

public Object put(Object key, Object value) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   for (Entry e = table[i]; e != null; e = e.next) {
     if (e.hash == hash && eq(k, e.key)) {
       Object oldValue = e.value;
       e.value = value;
       e.recordAccess(this);
       return oldValue;
     }
   }
   modCount++;
   addEntry(hash, k, value, i);
   return null;
}
首先获得hash索引位置,如果该位置的引用为null,那么直接插入一个映射,返回null。如果此处的引用不是null,必须遍历链表,如果找到一个相同的key,那么就更新该value,同时返回原来的value值。如果遍历完了没有找到,说明该key值不存在,还是插入一个映射。如果hash值足够离散的话,也就是说该索引没有被使用的话,那么不不用遍历链表了。相反,如果hash值不离散,极端的说如果是常数的话,所有的映射都会在这一个链表上,效率会极其低下。这里举一个最简单的例子,写两
个不同的类作为key插入到HashMap中,效率会远远不同。
class Good{
  int i;
  public Good(int i){
   this.i=i;
  }
  public boolean equals(Object o){
   return (o instanceof Good) && (this.i==((Good)o).i)
  }
  public int hashCode(){
   return i;
  }
}
class Bad{
  int i;
  public Good(int i){
    this.i=i;
  }
  public boolean equals(Object o){
    return (o instanceof Good) && (this.i==((Good)o).i)
  }
  public int hashCode(){
   return 0;
  }
}
执行代码:
Map m1=new HashMap();
Map m2=new HashMap();
for(int i=0;i<100;i++){
  m1.put(new Good(i),new Integer(i)); //这里效率非常高
}
for(int i=0;i<100;i++){
  m2.put(new Bad(i),new Integer(i)); //这里几乎要崩溃
}
上面的是两个非常极端的例子,执行一下就知道差别有多大。

private void putForCreate(Object key, Object value) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   for (Entry e = table[i]; e != null; e = e.next) {
     if (e.hash == hash && eq(k, e.key)) {
        e.value = value;
        return;
     }
   }
   createEntry(hash, k, value, i);
}

void putAllForCreate(Map m) {
   for (Iterator i = m.entrySet().iterator(); i.hasNext(); ) {
     Map.Entry e = (Map.Entry) i.next();
     putForCreate(e.getKey(), e.getValue());
   }
}
上面的两个方法是被构造函数和clone方法调用的。

void resize(int newCapacity) {
   Entry[] oldTable = table;
   int oldCapacity = oldTable.length;
   if (size < threshold || oldCapacity > newCapacity)
     return;
   Entry[] newTable = new Entry[newCapacity];
   transfer(newTable);
   table = newTable;
   threshold = (int)(newCapacity * loadFactor);
}
这个方法在需要的时候重新分配空间,相当于ArrayList的ensureCapacity方法,不过这个更加复杂。

void transfer(Entry[] newTable) {
   Entry[] src = table;
   int newCapacity = newTable.length;
   for (int j = 0; j < src.length; j++) {
     Entry e = src[j];
     if (e != null) {
       src[j] = null;
       do {
          Entry next = e.next;
          int i = indexFor(e.hash, newCapacity);
          e.next = newTable[i];
          newTable[i] = e;
          e = next;
       } while (e != null);
     }
   }
}
遍历原来的数组,如果该Entry不是null的话,说明有映射,然后遍历这个链表,把所有的映射插入到新的数组中,注意这里要从新计算索引位置。

public void putAll(Map t) {
   int n = t.size();
   if (n == 0)
     return;
   if (n >= threshold) {
     n = (int)(n / loadFactor + 1);
     if (n > MAXIMUM_CAPACITY)
       n = MAXIMUM_CAPACITY;
     int capacity = table.length;
     while (capacity < n) capacity <<= 1;
       resize(capacity);
   }
   for (Iterator i = t.entrySet().iterator(); i.hasNext(); ) {
     Map.Entry e = (Map.Entry) i.next();
     put(e.getKey(), e.getValue());
   }
}
这个方法先确定是否需要扩大空间,然后循环调用put方法。

public Object remove(Object key) {
   Entry e = removeEntryForKey(key);
   return (e == null ? e : e.value);
}

Entry removeEntryForKey(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry prev = table[i];
   Entry e = prev;
   while (e != null) {  如果e==null表示不存在
     Entry next = e.next;
     if (e.hash == hash && eq(k, e.key)) {
       modCount++;
       size--;
       if (prev == e)
         table[i] = next; 链表的第一个元素就是要删除的,这里最好加一句 e.next=null.
       else
         prev.next = next; 存在担不是链表的第一个元素, 这里最好加一句 e.next=null.
       e.recordRemoval(this);
       return e;
     }
     prev = e;
     e = next;
   }
  return e;   这里其实就是return null;
}
这个方法其实也不复杂,也是遍历链表,这里建议加一句e.next=null,可以改为
if(prev==e)
  table[i]=next;
else
  prev.next=next;
e.next=null; 这一句是多加的,可以提高效率。
这里简单说明我的看法:
因为e是被删除的节点,删除它其实就是指向它的指针指向它的后面一个节点。所以e可以作为GC回收的对象。
可以e还有一个next指针指向我们的数据,如果e没有被回收。而且此时e.next指向的节点也变为没用的了,但是
却有一个它的引用(e.next),所以虽然e的下一个节点没用了,但是却不能作为GC回收的对象,除非e先被回收。
虽然不一定会引起很大的问题,但是至少会影响GC的回收效率。就像数据库中的外键引用一样,删除起来很麻烦呀。

Entry removeMapping(Object o) {
   if (!(o instanceof Map.Entry))
     return null;
   Map.Entry entry = (Map.Entry)o;
   Object k = maskNull(entry.getKey());
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry prev = table[i];
   Entry e = prev;
   while (e != null) {
     Entry next = e.next;
     if (e.hash == hash && e.equals(entry)) {
       modCount++;
       size--;
       if (prev == e)
         table[i] = next;
       else
         prev.next = next;
       e.recordRemoval(this);
       return e;
      }
      prev = e;
      e = next;
   }
   return e;
}
这个方法和上面的一样。

public void clear() {
   modCount++;
   Entry tab[] = table;
   for (int i = 0; i < tab.length; i++)
     tab[i] = null;
   size = 0;
}
同样可以改进

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;
}

private boolean containsNullValue() {
   Entry tab[] = table;
   for (int i = 0; i < tab.length ; i++)
     for (Entry e = tab[i] ; e != null ; e = e.next)
       if (e.value == null) return true;
   return false;
}

public Object clone() {
   HashMap result = null;
   try {
     result = (HashMap)super.clone();
   }
   catch (CloneNotSupportedException e) { // assert false; }
   result.table = new Entry[table.length];
   result.entrySet = null;
   result.modCount = 0;
   result.size = 0;
   result.init();
   result.putAllForCreate(this);
   return result;
}

static class Entry implements Map.Entry {
   final Object key;
   Object value;
   final int hash;
   Entry next;
   Entry(int h, Object k, Object v, Entry n) {
     value = v;
     next = n;
     key = k;
     hash = h;
   }
   public Object getKey() {
     return unmaskNull(key);
   }
   public Object getValue() {
     return value;
   }
   public Object setValue(Object newValue) {
      Object oldValue = value;
      value = newValue;
      return oldValue;
   }
   public boolean equals(Object o) {
     if (!(o instanceof Map.Entry)) return false;
     Map.Entry e = (Map.Entry)o;
     Object k1 = getKey();
     Object k2 = e.getKey();
     if (k1 == k2 || (k1 != null && k1.equals(k2))) {
       Object v1 = getValue();
       Object v2 = e.getValue();
       if (v1 == v2 || (v1 != null && v1.equals(v2))) return true;
     }
     return false;
    }
    public int hashCode() {
      return (key==NULL_KEY ? 0 : key.hashCode()) ^ (value==null ? 0 : value.hashCode());
    }
    public String toString() {
      return getKey() + "=" + getValue();
    }
    void recordAccess(HashMap m) { }
    void recordRemoval(HashMap m) { }
}
一个静态内部类

void addEntry(int hash, Object key, Object value, int bucketIndex) {
    table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);
    if (size++ >= threshold)
      resize(2 * table.length);
}
注意这个方法,插入连表的头。
可以写成这样更好理解:
Entry oldHead=table[bucketIndex];
Entry newHead = new Entry(hash,key,value,oldHead);
table[bucketIndex]=newHead;

void createEntry(int hash, Object key, Object value, int bucketIndex) {
   table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);
   size++;
}

private abstract class HashIterator implements Iterator {
   Entry next;
   int expectedModCount;
   int index;
   Entry current;
   HashIterator() {
     expectedModCount = modCount;
     Entry[] t = table;
     int i = t.length;
     Entry n = null;
     if (size != 0) {
       while (i > 0 && (n = t[--i]) == null) ;
     }
     next = n;
     index = i;
   }
   public boolean hasNext() {
     return next != null;
   }
   Entry nextEntry() {
     if (modCount != expectedModCount)
       throw new ConcurrentModificationException();
     Entry e = next;
    if (e == null)
       throw new NoSuchElementException();
     Entry n = e.next;
     Entry[] t = table;
     int i = index;
     while (n == null && i > 0)
        n = t[--i]; index = i;
     next = n;
     return current = e;
   }
   public void remove() {
     if (current == null)
       throw new IllegalStateException();
     if (modCount != expectedModCount)
       throw new ConcurrentModificationException();
     Object k = current.key;
     current = null;
     HashMap.this.removeEntryForKey(k);
     expectedModCount = modCount;
   }
}

private class ValueIterator extends HashIterator {
   public Object next() {
     return nextEntry().value;
   }
}

private class KeyIterator extends HashIterator {
   public Object next() {
     return nextEntry().getKey();
   }
}

private class EntryIterator extends HashIterator {
   public Object next() {
     return nextEntry();
   }
}

Iterator newKeyIterator() {
   return new KeyIterator();
}

Iterator newValueIterator() {
   return new ValueIterator();
}

Iterator newEntryIterator() {  
   return new EntryIterator();
}

private transient Set entrySet = null;

public Set keySet() {
   Set ks = keySet;
   return (ks != null ? ks : (keySet = new KeySet()));
}

private class KeySet extends AbstractSet {
   public Iterator iterator() {
     return newKeyIterator();
   }
   public int size() {
     return size;
   }
   public boolean contains(Object o) {
     return containsKey(o);
   }
   public boolean remove(Object o) {
     return HashMap.this.removeEntryForKey(o) != null;
   }
   public void clear() {
     HashMap.this.clear();
   }
}

public Collection values() {
   Collection vs = values; return (vs != null ? vs : (values = new Values()));
}

private class Values extends AbstractCollection {
   public Iterator iterator() {
     return newValueIterator();
   }
   public int size() {
     return size;
   }
   public boolean contains(Object o) {
     return containsValue(o);
   }
   public void clear() {
     HashMap.this.clear();
   }
}

public Set entrySet() {
   Set es = entrySet;
   return (es != null ? es : (entrySet = new EntrySet()));
}

private class EntrySet extends AbstractSet {
   public Iterator iterator() {
     return newEntryIterator();
   }
   public boolean contains(Object o) {
     if (!(o instanceof Map.Entry))
       return false;
     Map.Entry e = (Map.Entry)o;
     Entry candidate = getEntry(e.getKey());
     return candidate != null && candidate.equals(e);
   }
   public boolean remove(Object o) {
     return removeMapping(o) != null;
   }
   public int size() {
     return size;
   }
   public void clear() {
     HashMap.this.clear();
   }
}

private void writeObject(java.io.ObjectOutputStream s) throws IOException {
   s.defaultWriteObject();
   s.writeInt(table.length);
   s.writeInt(size);
   for (Iterator i = entrySet().iterator(); i.hasNext(); ) {
     Map.Entry e = (Map.Entry) i.next();
     s.writeObject(e.getKey());
     s.writeObject(e.getValue());
   }
}

private static final long serialVersionUID = 362498820763181265L;

private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
   s.defaultReadObject();
   int numBuckets = s.readInt();
   table = new Entry[numBuckets];
   init();
   size = s.readInt(); for (int i=0;
   for (int i=0; i<size; i++) {
     Object key = s.readObject();
     Object value = s.readObject(); 
     putForCreate(key, value);
   }
}

int capacity() {
  return table.length;
}
float loadFactor() {
   return loadFactor;
}

HashMap是Java新Collection Framework中用来代替HashTable的一个实现,HashMap和HashTable的区别是: HashMap是未经同步的,而且允许null值。HashTable继承Dictionary,而且使用了Enumeration,所以被建议不要使用。
HashMap的声明如下:
public class HashMap extends AbstractMap implements Map, Cloneable,Serializable
有关AbstractMap:http://blog.youkuaiyun.com/treeroot/archive/2004/09/20/110343.aspx
有关Map:http://blog.youkuaiyun.com/treeroot/archive/2004/09/20/110331.aspx
有关Cloneable:http://blog.youkuaiyun.com/treeroot/archive/2004/09/07/96936.aspx
这个类比较复杂,这里只是重点分析了几个方法,特别是后面涉及到很多内部类都没有解释
不过都比较简单。

static final int DEFAULT_INITIAL_CAPACITY = 16; 默认初始化大小

static final int MAXIMUM_CAPACITY = 1 << 30; 最大初始化大小

static final float DEFAULT_LOAD_FACTOR = 0.75f; 默认加载因子

transient Entry[] table; 一个Entry类型的数组,数组的长度为2的指数。

transient int size; 映射的个数

int threshold; 下一次扩容时的值

final float loadFactor; 加载因子

transient volatile int modCount; 修改次数

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);
   int capacity = 1;
   while (capacity < initialCapacity)
     capacity <<= 1;
   this.loadFactor = loadFactor;
   threshold = (int)(capacity * loadFactor);
   table = new Entry[capacity];
   init();
}

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

public HashMap() {
   this.loadFactor = DEFAULT_LOAD_FACTOR;
   threshold = (int)(DEFAULT_INITIAL_CAPACITY);
      注意:这里应该是一个失误! 应该是:threshold =(int)(DEFAULT_INITIAL_CAPACITY * loadFactor);
   table = new Entry[DEFAULT_INITIAL_CAPACITY];
   init();
}

public HashMap(Map m) {
   this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY),                                   DEFAULT_LOAD_FACTOR);
   putAllForCreate(m);
}

void init() {}

static final Object NULL_KEY = new Object();

static Object maskNull(Object key){
   return (key == null ? NULL_KEY : key);
}

static Object unmaskNull(Object key) {
   return (key == NULL_KEY ? null : key);
}

static int hash(Object x) {
   int h = x.hashCode();
   h += ~(h << 9);
   h ^= (h >>> 14);
   h += (h << 4);
   h ^= (h >>> 10);
return h;
}
在HashTable中没有这个方法,也就是说HashTable中是直接用对象的hashCode值,但是HashMap做了改进 用这个算法来获得哈希值。

static boolean eq(Object x, Object y) {
   return x == y || x.equals(y);
}

static int indexFor(int h, int length) {
   return h & (length-1);
}
根据哈希值和数组的长度来返回该hash值在数组中的位置,只是简单的与关系。

public int size() {
   return size;
}

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

public Object get(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry e = table[i];
   while (true) {
     if (e == null) return e;
     if (e.hash == hash && eq(k, e.key)) return e.value;
     e = e.next;
   }
}
这个方法是获取数据的方法,首先获得哈希值,这里把null值掩饰了,并且hash值经过函数hash()修正。 然后计算该哈希值在数组中的索引值。如果该索引处的引用为null,表示HashMap中不存在这个映射。 否则的话遍历整个链表,这里找到了就返回,如果没有找到就遍历到链表末尾,返回null。这里的比较是这样的:e.hash==hash && eq(k,e.key) 也就是说如果hash不同就肯定认为不相等,eq就被短路了,只有在 hash相同的情况下才调用equals方法。现在我们该明白Object中说的如果两个对象equals返回true,他们的 hashCode应该相同的道理了吧。假如两个对象调用equals返回true,但是hashCode不一样,那么在HashMap 里就认为他们不相等。

public boolean containsKey(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry e = table[i];
   while (e != null) {
     if (e.hash == hash && eq(k, e.key)) return true;
     e = e.next;
   }
  return false;
}
这个方法比上面的简单,先找到哈希位置,再遍历整个链表,如果找到就返回true。

Entry getEntry(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry e = table[i];
   while (e != null && !(e.hash == hash && eq(k, e.key)))
     e = e.next;
   return e;
}
这个方法根据key值返回Entry节点,也是先获得索引位置,再遍历链表,如果没有找到返回的是null。

public Object put(Object key, Object value) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   for (Entry e = table[i]; e != null; e = e.next) {
     if (e.hash == hash && eq(k, e.key)) {
       Object oldValue = e.value;
       e.value = value;
       e.recordAccess(this);
       return oldValue;
     }
   }
   modCount++;
   addEntry(hash, k, value, i);
   return null;
}
首先获得hash索引位置,如果该位置的引用为null,那么直接插入一个映射,返回null。如果此处的引用不是null,必须遍历链表,如果找到一个相同的key,那么就更新该value,同时返回原来的value值。如果遍历完了没有找到,说明该key值不存在,还是插入一个映射。如果hash值足够离散的话,也就是说该索引没有被使用的话,那么不不用遍历链表了。相反,如果hash值不离散,极端的说如果是常数的话,所有的映射都会在这一个链表上,效率会极其低下。这里举一个最简单的例子,写两
个不同的类作为key插入到HashMap中,效率会远远不同。
class Good{
  int i;
  public Good(int i){
   this.i=i;
  }
  public boolean equals(Object o){
   return (o instanceof Good) && (this.i==((Good)o).i)
  }
  public int hashCode(){
   return i;
  }
}
class Bad{
  int i;
  public Good(int i){
    this.i=i;
  }
  public boolean equals(Object o){
    return (o instanceof Good) && (this.i==((Good)o).i)
  }
  public int hashCode(){
   return 0;
  }
}
执行代码:
Map m1=new HashMap();
Map m2=new HashMap();
for(int i=0;i<100;i++){
  m1.put(new Good(i),new Integer(i)); //这里效率非常高
}
for(int i=0;i<100;i++){
  m2.put(new Bad(i),new Integer(i)); //这里几乎要崩溃
}
上面的是两个非常极端的例子,执行一下就知道差别有多大。

private void putForCreate(Object key, Object value) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   for (Entry e = table[i]; e != null; e = e.next) {
     if (e.hash == hash && eq(k, e.key)) {
        e.value = value;
        return;
     }
   }
   createEntry(hash, k, value, i);
}

void putAllForCreate(Map m) {
   for (Iterator i = m.entrySet().iterator(); i.hasNext(); ) {
     Map.Entry e = (Map.Entry) i.next();
     putForCreate(e.getKey(), e.getValue());
   }
}
上面的两个方法是被构造函数和clone方法调用的。

void resize(int newCapacity) {
   Entry[] oldTable = table;
   int oldCapacity = oldTable.length;
   if (size < threshold || oldCapacity > newCapacity)
     return;
   Entry[] newTable = new Entry[newCapacity];
   transfer(newTable);
   table = newTable;
   threshold = (int)(newCapacity * loadFactor);
}
这个方法在需要的时候重新分配空间,相当于ArrayList的ensureCapacity方法,不过这个更加复杂。

void transfer(Entry[] newTable) {
   Entry[] src = table;
   int newCapacity = newTable.length;
   for (int j = 0; j < src.length; j++) {
     Entry e = src[j];
     if (e != null) {
       src[j] = null;
       do {
          Entry next = e.next;
          int i = indexFor(e.hash, newCapacity);
          e.next = newTable[i];
          newTable[i] = e;
          e = next;
       } while (e != null);
     }
   }
}
遍历原来的数组,如果该Entry不是null的话,说明有映射,然后遍历这个链表,把所有的映射插入到新的数组中,注意这里要从新计算索引位置。

public void putAll(Map t) {
   int n = t.size();
   if (n == 0)
     return;
   if (n >= threshold) {
     n = (int)(n / loadFactor + 1);
     if (n > MAXIMUM_CAPACITY)
       n = MAXIMUM_CAPACITY;
     int capacity = table.length;
     while (capacity < n) capacity <<= 1;
       resize(capacity);
   }
   for (Iterator i = t.entrySet().iterator(); i.hasNext(); ) {
     Map.Entry e = (Map.Entry) i.next();
     put(e.getKey(), e.getValue());
   }
}
这个方法先确定是否需要扩大空间,然后循环调用put方法。

public Object remove(Object key) {
   Entry e = removeEntryForKey(key);
   return (e == null ? e : e.value);
}

Entry removeEntryForKey(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry prev = table[i];
   Entry e = prev;
   while (e != null) {  如果e==null表示不存在
     Entry next = e.next;
     if (e.hash == hash && eq(k, e.key)) {
       modCount++;
       size--;
       if (prev == e)
         table[i] = next; 链表的第一个元素就是要删除的,这里最好加一句 e.next=null.
       else
         prev.next = next; 存在担不是链表的第一个元素, 这里最好加一句 e.next=null.
       e.recordRemoval(this);
       return e;
     }
     prev = e;
     e = next;
   }
  return e;   这里其实就是return null;
}
这个方法其实也不复杂,也是遍历链表,这里建议加一句e.next=null,可以改为
if(prev==e)
  table[i]=next;
else
  prev.next=next;
e.next=null; 这一句是多加的,可以提高效率。
这里简单说明我的看法:
因为e是被删除的节点,删除它其实就是指向它的指针指向它的后面一个节点。所以e可以作为GC回收的对象。
可以e还有一个next指针指向我们的数据,如果e没有被回收。而且此时e.next指向的节点也变为没用的了,但是
却有一个它的引用(e.next),所以虽然e的下一个节点没用了,但是却不能作为GC回收的对象,除非e先被回收。
虽然不一定会引起很大的问题,但是至少会影响GC的回收效率。就像数据库中的外键引用一样,删除起来很麻烦呀。

Entry removeMapping(Object o) {
   if (!(o instanceof Map.Entry))
     return null;
   Map.Entry entry = (Map.Entry)o;
   Object k = maskNull(entry.getKey());
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry prev = table[i];
   Entry e = prev;
   while (e != null) {
     Entry next = e.next;
     if (e.hash == hash && e.equals(entry)) {
       modCount++;
       size--;
       if (prev == e)
         table[i] = next;
       else
         prev.next = next;
       e.recordRemoval(this);
       return e;
      }
      prev = e;
      e = next;
   }
   return e;
}
这个方法和上面的一样。

public void clear() {
   modCount++;
   Entry tab[] = table;
   for (int i = 0; i < tab.length; i++)
     tab[i] = null;
   size = 0;
}
同样可以改进

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;
}

private boolean containsNullValue() {
   Entry tab[] = table;
   for (int i = 0; i < tab.length ; i++)
     for (Entry e = tab[i] ; e != null ; e = e.next)
       if (e.value == null) return true;
   return false;
}

public Object clone() {
   HashMap result = null;
   try {
     result = (HashMap)super.clone();
   }
   catch (CloneNotSupportedException e) { // assert false; }
   result.table = new Entry[table.length];
   result.entrySet = null;
   result.modCount = 0;
   result.size = 0;
   result.init();
   result.putAllForCreate(this);
   return result;
}

static class Entry implements Map.Entry {
   final Object key;
   Object value;
   final int hash;
   Entry next;
   Entry(int h, Object k, Object v, Entry n) {
     value = v;
     next = n;
     key = k;
     hash = h;
   }
   public Object getKey() {
     return unmaskNull(key);
   }
   public Object getValue() {
     return value;
   }
   public Object setValue(Object newValue) {
      Object oldValue = value;
      value = newValue;
      return oldValue;
   }
   public boolean equals(Object o) {
     if (!(o instanceof Map.Entry)) return false;
     Map.Entry e = (Map.Entry)o;
     Object k1 = getKey();
     Object k2 = e.getKey();
     if (k1 == k2 || (k1 != null && k1.equals(k2))) {
       Object v1 = getValue();
       Object v2 = e.getValue();
       if (v1 == v2 || (v1 != null && v1.equals(v2))) return true;
     }
     return false;
    }
    public int hashCode() {
      return (key==NULL_KEY ? 0 : key.hashCode()) ^ (value==null ? 0 : value.hashCode());
    }
    public String toString() {
      return getKey() + "=" + getValue();
    }
    void recordAccess(HashMap m) { }
    void recordRemoval(HashMap m) { }
}
一个静态内部类

void addEntry(int hash, Object key, Object value, int bucketIndex) {
    table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);
    if (size++ >= threshold)
      resize(2 * table.length);
}
注意这个方法,插入连表的头。
可以写成这样更好理解:
Entry oldHead=table[bucketIndex];
Entry newHead = new Entry(hash,key,value,oldHead);
table[bucketIndex]=newHead;

void createEntry(int hash, Object key, Object value, int bucketIndex) {
   table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);
   size++;
}

private abstract class HashIterator implements Iterator {
   Entry next;
   int expectedModCount;
   int index;
   Entry current;
   HashIterator() {
     expectedModCount = modCount;
     Entry[] t = table;
     int i = t.length;
     Entry n = null;
     if (size != 0) {
       while (i > 0 && (n = t[--i]) == null) ;
     }
     next = n;
     index = i;
   }
   public boolean hasNext() {
     return next != null;
   }
   Entry nextEntry() {
     if (modCount != expectedModCount)
       throw new ConcurrentModificationException();
     Entry e = next;
    if (e == null)
       throw new NoSuchElementException();
     Entry n = e.next;
     Entry[] t = table;
     int i = index;
     while (n == null && i > 0)
        n = t[--i]; index = i;
     next = n;
     return current = e;
   }
   public void remove() {
     if (current == null)
       throw new IllegalStateException();
     if (modCount != expectedModCount)
       throw new ConcurrentModificationException();
     Object k = current.key;
     current = null;
     HashMap.this.removeEntryForKey(k);
     expectedModCount = modCount;
   }
}

private class ValueIterator extends HashIterator {
   public Object next() {
     return nextEntry().value;
   }
}

private class KeyIterator extends HashIterator {
   public Object next() {
     return nextEntry().getKey();
   }
}

private class EntryIterator extends HashIterator {
   public Object next() {
     return nextEntry();
   }
}

Iterator newKeyIterator() {
   return new KeyIterator();
}

Iterator newValueIterator() {
   return new ValueIterator();
}

Iterator newEntryIterator() {  
   return new EntryIterator();
}

private transient Set entrySet = null;

public Set keySet() {
   Set ks = keySet;
   return (ks != null ? ks : (keySet = new KeySet()));
}

private class KeySet extends AbstractSet {
   public Iterator iterator() {
     return newKeyIterator();
   }
   public int size() {
     return size;
   }
   public boolean contains(Object o) {
     return containsKey(o);
   }
   public boolean remove(Object o) {
     return HashMap.this.removeEntryForKey(o) != null;
   }
   public void clear() {
     HashMap.this.clear();
   }
}

public Collection values() {
   Collection vs = values; return (vs != null ? vs : (values = new Values()));
}

private class Values extends AbstractCollection {
   public Iterator iterator() {
     return newValueIterator();
   }
   public int size() {
     return size;
   }
   public boolean contains(Object o) {
     return containsValue(o);
   }
   public void clear() {
     HashMap.this.clear();
   }
}

public Set entrySet() {
   Set es = entrySet;
   return (es != null ? es : (entrySet = new EntrySet()));
}

private class EntrySet extends AbstractSet {
   public Iterator iterator() {
     return newEntryIterator();
   }
   public boolean contains(Object o) {
     if (!(o instanceof Map.Entry))
       return false;
     Map.Entry e = (Map.Entry)o;
     Entry candidate = getEntry(e.getKey());
     return candidate != null && candidate.equals(e);
   }
   public boolean remove(Object o) {
     return removeMapping(o) != null;
   }
   public int size() {
     return size;
   }
   public void clear() {
     HashMap.this.clear();
   }
}

private void writeObject(java.io.ObjectOutputStream s) throws IOException {
   s.defaultWriteObject();
   s.writeInt(table.length);
   s.writeInt(size);
   for (Iterator i = entrySet().iterator(); i.hasNext(); ) {
     Map.Entry e = (Map.Entry) i.next();
     s.writeObject(e.getKey());
     s.writeObject(e.getValue());
   }
}

private static final long serialVersionUID = 362498820763181265L;

private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
   s.defaultReadObject();
   int numBuckets = s.readInt();
   table = new Entry[numBuckets];
   init();
   size = s.readInt(); for (int i=0;
   for (int i=0; i<size; i++) {
     Object key = s.readObject();
     Object value = s.readObject(); 
     putForCreate(key, value);
   }
}

int capacity() {
  return table.length;
}
float loadFactor() {
   return loadFactor;
}

  HashMap可谓JDK的一大实用工具,把各个Object映射起来,实现了“键--值”对应的快速存取。但实际里面做了些什么呢?

  在这之前,先介绍一下负载因子和容量的属性。大家都知道其实一个 HashMap 的实际容量就 因子*容量,其默认值是 16×0.75=12; 这个很重要,对效率很一定影响!当存入HashMap的对象超过这个容量时,HashMap 就会重新构造存取表。这就是一个大问题,我后面慢慢介绍,反正,如果你已经知道你大概要存放多少个对象,最好设为该实际容量的能接受的数字。

  两个关键的方法,put和get:

  先有这样一个概念,HashMap是声明了 Map,Cloneable, Serializable 接口,和继承了 AbstractMap 类,里面的 Iterator 其实主要都是其内部类HashIterator 和其他几个 iterator 类实现,当然还有一个很重要的继承了Map.Entry 的 Entry 内部类,由于大家都有源代码,大家有兴趣可以看看这部分,我主要想说明的是 Entry 内部类。它包含了hash,value,key 和next 这四个属性,很重要。put的源码如下

public Object put(Object key, Object value) {
Object k = maskNull(key);

  这个就是判断键值是否为空,并不很深奥,其实如果为空,它会返回一个static Object 作为键值,这就是为什么HashMap允许空键值的原因。

int hash = hash(k);
int i = indexFor(hash, table.length);

  这连续的两步就是 HashMap 最牛的地方!研究完我都汗颜了,其中 hash 就是通过 key 这个Object的 hashcode 进行 hash,然后通过 indexFor 获得在Object table的索引值。

  table???不要惊讶,其实HashMap也神不到哪里去,它就是用 table 来放的。最牛的就是用 hash 能正确的返回索引。其中的hash算法,我跟JDK的作者 Doug 联系过,他建议我看看《The art of programing vol3》可恨的是,我之前就一直在找,我都找不到,他这样一提,我就更加急了,可惜口袋空空啊!!!

  不知道大家有没有留意 put 其实是一个有返回的方法,它会把相同键值的 put 覆盖掉并返回旧的值!如下方法彻底说明了 HashMap 的结构,其实就是一个表加上在相应位置的Entry的链表:

for (Entry e = table[i]; e != null; e = e.next) {
 if (e.hash == hash && eq(k, e.key)) {
  Object oldvalue = e.value;
  e.value = value; //把新的值赋予给对应键值。
  e.recordAccess(this); //空方法,留待实现
  return oldvalue; //返回相同键值的对应的旧的值。
 }
}
modCount++; //结构性更改的次数
addEntry(hash, k, value, i); //添加新元素,关键所在!
return null; //没有相同的键值返回
}

  我们把关键的方法拿出来分析:

void addEntry(int hash, Object key, Object value, int bucketIndex) {
table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);

  因为 hash 的算法有可能令不同的键值有相同的hash码并有相同的table索引,如:key=“33”和key=Object g的hash都是-8901334,那它经过indexfor之后的索引一定都为i,这样在new的时候这个Entry的next就会指向这个原本的table[i],再有下一个也如此,形成一个链表,和put的循环对定e.next获得旧的值。到这里,HashMap的结构,大家也十分明白了吧?

if (size++ >= threshold) //这个threshold就是能实际容纳的量
resize(2 * table.length); //超出这个容量就会将Object table重构

  所谓的重构也不神,就是建一个两倍大的table(我在别的论坛上看到有人说是两倍加1,把我骗了),然后再一个个indexfor进去!注意!!这就是效率!!如果你能让你的HashMap不需要重构那么多次,效率会大大提高!

  说到这里也差不多了,get比put简单得多,大家,了解put,get也差不了多少了。对于collections我是认为,它是适合广泛的,当不完全适合特有的,如果大家的程序需要特殊的用途,自己写吧,其实很简单。(作者是这样跟我说的,他还建议我用LinkedHashMap,我看了源码以后发现,LinkHashMap其实就是继承HashMap的,然后override相应的方法,有兴趣的同人,自己looklook)建个 Object table,写相应的算法,就ok啦。

  举个例子吧,像 Vector,list 啊什么的其实都很简单,最多就多了的同步的声明,其实如果要实现像Vector那种,插入,删除不多的,可以用一个Object table来实现,按索引存取,添加等。

  如果插入,删除比较多的,可以建两个Object table,然后每个元素用含有next结构的,一个table存,如果要插入到i,但是i已经有元素,用next连起来,然后size++,并在另一个table记录其位置。
源码分析:HashMap
     

HashMap是Java新Collection Framework中用来代替HashTable的一个实现,HashMap和HashTable的区别是: HashMap是未经同步的,而且允许null值。HashTable继承Dictionary,而且使用了Enumeration,所以被建议不要使用。
HashMap的声明如下:
public class HashMap extends AbstractMap implements Map, Cloneable,Serializable
有关AbstractMap:http://blog.youkuaiyun.com/treeroot/archive/2004/09/20/110343.aspx
有关Map:http://blog.youkuaiyun.com/treeroot/archive/2004/09/20/110331.aspx
有关Cloneable:http://blog.youkuaiyun.com/treeroot/archive/2004/09/07/96936.aspx
这个类比较复杂,这里只是重点分析了几个方法,特别是后面涉及到很多内部类都没有解释
不过都比较简单。

static final int DEFAULT_INITIAL_CAPACITY = 16; 默认初始化大小

static final int MAXIMUM_CAPACITY = 1 << 30; 最大初始化大小

static final float DEFAULT_LOAD_FACTOR = 0.75f; 默认加载因子

transient Entry[] table; 一个Entry类型的数组,数组的长度为2的指数。

transient int size; 映射的个数

int threshold; 下一次扩容时的值

final float loadFactor; 加载因子

transient volatile int modCount; 修改次数

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);
   int capacity = 1;
   while (capacity < initialCapacity)
     capacity <<= 1;
   this.loadFactor = loadFactor;
   threshold = (int)(capacity * loadFactor);
   table = new Entry[capacity];
   init();
}

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

public HashMap() {
   this.loadFactor = DEFAULT_LOAD_FACTOR;
   threshold = (int)(DEFAULT_INITIAL_CAPACITY);
      注意:这里应该是一个失误! 应该是:threshold =(int)(DEFAULT_INITIAL_CAPACITY * loadFactor);
   table = new Entry[DEFAULT_INITIAL_CAPACITY];
   init();
}

public HashMap(Map m) {
   this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY),                                   DEFAULT_LOAD_FACTOR);
   putAllForCreate(m);
}

void init() {}

static final Object NULL_KEY = new Object();

static Object maskNull(Object key){
   return (key == null ? NULL_KEY : key);
}

static Object unmaskNull(Object key) {
   return (key == NULL_KEY ? null : key);
}

static int hash(Object x) {
   int h = x.hashCode();
   h += ~(h << 9);
   h ^= (h >>> 14);
   h += (h << 4);
   h ^= (h >>> 10);
return h;
}
在HashTable中没有这个方法,也就是说HashTable中是直接用对象的hashCode值,但是HashMap做了改进 用这个算法来获得哈希值。

static boolean eq(Object x, Object y) {
   return x == y || x.equals(y);
}

static int indexFor(int h, int length) {
   return h & (length-1);
}
根据哈希值和数组的长度来返回该hash值在数组中的位置,只是简单的与关系。

public int size() {
   return size;
}

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

public Object get(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry e = table[i];
   while (true) {
     if (e == null) return e;
     if (e.hash == hash && eq(k, e.key)) return e.value;
     e = e.next;
   }
}
这个方法是获取数据的方法,首先获得哈希值,这里把null值掩饰了,并且hash值经过函数hash()修正。 然后计算该哈希值在数组中的索引值。如果该索引处的引用为null,表示HashMap中不存在这个映射。 否则的话遍历整个链表,这里找到了就返回,如果没有找到就遍历到链表末尾,返回null。这里的比较是这样的:e.hash==hash && eq(k,e.key) 也就是说如果hash不同就肯定认为不相等,eq就被短路了,只有在 hash相同的情况下才调用equals方法。现在我们该明白Object中说的如果两个对象equals返回true,他们的 hashCode应该相同的道理了吧。假如两个对象调用equals返回true,但是hashCode不一样,那么在HashMap 里就认为他们不相等。

public boolean containsKey(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry e = table[i];
   while (e != null) {
     if (e.hash == hash && eq(k, e.key)) return true;
     e = e.next;
   }
  return false;
}
这个方法比上面的简单,先找到哈希位置,再遍历整个链表,如果找到就返回true。

Entry getEntry(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry e = table[i];
   while (e != null && !(e.hash == hash && eq(k, e.key)))
     e = e.next;
   return e;
}
这个方法根据key值返回Entry节点,也是先获得索引位置,再遍历链表,如果没有找到返回的是null。

public Object put(Object key, Object value) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   for (Entry e = table[i]; e != null; e = e.next) {
     if (e.hash == hash && eq(k, e.key)) {
       Object oldValue = e.value;
       e.value = value;
       e.recordAccess(this);
       return oldValue;
     }
   }
   modCount++;
   addEntry(hash, k, value, i);
   return null;
}
首先获得hash索引位置,如果该位置的引用为null,那么直接插入一个映射,返回null。如果此处的引用不是null,必须遍历链表,如果找到一个相同的key,那么就更新该value,同时返回原来的value值。如果遍历完了没有找到,说明该key值不存在,还是插入一个映射。如果hash值足够离散的话,也就是说该索引没有被使用的话,那么不不用遍历链表了。相反,如果hash值不离散,极端的说如果是常数的话,所有的映射都会在这一个链表上,效率会极其低下。这里举一个最简单的例子,写两
个不同的类作为key插入到HashMap中,效率会远远不同。
class Good{
  int i;
  public Good(int i){
   this.i=i;
  }
  public boolean equals(Object o){
   return (o instanceof Good) && (this.i==((Good)o).i)
  }
  public int hashCode(){
   return i;
  }
}
class Bad{
  int i;
  public Good(int i){
    this.i=i;
  }
  public boolean equals(Object o){
    return (o instanceof Good) && (this.i==((Good)o).i)
  }
  public int hashCode(){
   return 0;
  }
}
执行代码:
Map m1=new HashMap();
Map m2=new HashMap();
for(int i=0;i<100;i++){
  m1.put(new Good(i),new Integer(i)); //这里效率非常高
}
for(int i=0;i<100;i++){
  m2.put(new Bad(i),new Integer(i)); //这里几乎要崩溃
}
上面的是两个非常极端的例子,执行一下就知道差别有多大。

private void putForCreate(Object key, Object value) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   for (Entry e = table[i]; e != null; e = e.next) {
     if (e.hash == hash && eq(k, e.key)) {
        e.value = value;
        return;
     }
   }
   createEntry(hash, k, value, i);
}

void putAllForCreate(Map m) {
   for (Iterator i = m.entrySet().iterator(); i.hasNext(); ) {
     Map.Entry e = (Map.Entry) i.next();
     putForCreate(e.getKey(), e.getValue());
   }
}
上面的两个方法是被构造函数和clone方法调用的。

void resize(int newCapacity) {
   Entry[] oldTable = table;
   int oldCapacity = oldTable.length;
   if (size < threshold || oldCapacity > newCapacity)
     return;
   Entry[] newTable = new Entry[newCapacity];
   transfer(newTable);
   table = newTable;
   threshold = (int)(newCapacity * loadFactor);
}
这个方法在需要的时候重新分配空间,相当于ArrayList的ensureCapacity方法,不过这个更加复杂。

void transfer(Entry[] newTable) {
   Entry[] src = table;
   int newCapacity = newTable.length;
   for (int j = 0; j < src.length; j++) {
     Entry e = src[j];
     if (e != null) {
       src[j] = null;
       do {
          Entry next = e.next;
          int i = indexFor(e.hash, newCapacity);
          e.next = newTable[i];
          newTable[i] = e;
          e = next;
       } while (e != null);
     }
   }
}
遍历原来的数组,如果该Entry不是null的话,说明有映射,然后遍历这个链表,把所有的映射插入到新的数组中,注意这里要从新计算索引位置。

public void putAll(Map t) {
   int n = t.size();
   if (n == 0)
     return;
   if (n >= threshold) {
     n = (int)(n / loadFactor + 1);
     if (n > MAXIMUM_CAPACITY)
       n = MAXIMUM_CAPACITY;
     int capacity = table.length;
     while (capacity < n) capacity <<= 1;
       resize(capacity);
   }
   for (Iterator i = t.entrySet().iterator(); i.hasNext(); ) {
     Map.Entry e = (Map.Entry) i.next();
     put(e.getKey(), e.getValue());
   }
}
这个方法先确定是否需要扩大空间,然后循环调用put方法。

public Object remove(Object key) {
   Entry e = removeEntryForKey(key);
   return (e == null ? e : e.value);
}

Entry removeEntryForKey(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry prev = table[i];
   Entry e = prev;
   while (e != null) {  如果e==null表示不存在
     Entry next = e.next;
     if (e.hash == hash && eq(k, e.key)) {
       modCount++;
       size--;
       if (prev == e)
         table[i] = next; 链表的第一个元素就是要删除的,这里最好加一句 e.next=null.
       else
         prev.next = next; 存在担不是链表的第一个元素, 这里最好加一句 e.next=null.
       e.recordRemoval(this);
       return e;
     }
     prev = e;
     e = next;
   }
  return e;   这里其实就是return null;
}
这个方法其实也不复杂,也是遍历链表,这里建议加一句e.next=null,可以改为
if(prev==e)
  table[i]=next;
else
  prev.next=next;
e.next=null; 这一句是多加的,可以提高效率。
这里简单说明我的看法:
因为e是被删除的节点,删除它其实就是指向它的指针指向它的后面一个节点。所以e可以作为GC回收的对象。
可以e还有一个next指针指向我们的数据,如果e没有被回收。而且此时e.next指向的节点也变为没用的了,但是
却有一个它的引用(e.next),所以虽然e的下一个节点没用了,但是却不能作为GC回收的对象,除非e先被回收。
虽然不一定会引起很大的问题,但是至少会影响GC的回收效率。就像数据库中的外键引用一样,删除起来很麻烦呀。

Entry removeMapping(Object o) {
   if (!(o instanceof Map.Entry))
     return null;
   Map.Entry entry = (Map.Entry)o;
   Object k = maskNull(entry.getKey());
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry prev = table[i];
   Entry e = prev;
   while (e != null) {
     Entry next = e.next;
     if (e.hash == hash && e.equals(entry)) {
       modCount++;
       size--;
       if (prev == e)
         table[i] = next;
       else
         prev.next = next;
       e.recordRemoval(this);
       return e;
      }
      prev = e;
      e = next;
   }
   return e;
}
这个方法和上面的一样。

public void clear() {
   modCount++;
   Entry tab[] = table;
   for (int i = 0; i < tab.length; i++)
     tab[i] = null;
   size = 0;
}
同样可以改进

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;
}

private boolean containsNullValue() {
   Entry tab[] = table;
   for (int i = 0; i < tab.length ; i++)
     for (Entry e = tab[i] ; e != null ; e = e.next)
       if (e.value == null) return true;
   return false;
}

public Object clone() {
   HashMap result = null;
   try {
     result = (HashMap)super.clone();
   }
   catch (CloneNotSupportedException e) { // assert false; }
   result.table = new Entry[table.length];
   result.entrySet = null;
   result.modCount = 0;
   result.size = 0;
   result.init();
   result.putAllForCreate(this);
   return result;
}

static class Entry implements Map.Entry {
   final Object key;
   Object value;
   final int hash;
   Entry next;
   Entry(int h, Object k, Object v, Entry n) {
     value = v;
     next = n;
     key = k;
     hash = h;
   }
   public Object getKey() {
     return unmaskNull(key);
   }
   public Object getValue() {
     return value;
   }
   public Object setValue(Object newValue) {
      Object oldValue = value;
      value = newValue;
      return oldValue;
   }
   public boolean equals(Object o) {
     if (!(o instanceof Map.Entry)) return false;
     Map.Entry e = (Map.Entry)o;
     Object k1 = getKey();
     Object k2 = e.getKey();
     if (k1 == k2 || (k1 != null && k1.equals(k2))) {
       Object v1 = getValue();
       Object v2 = e.getValue();
       if (v1 == v2 || (v1 != null && v1.equals(v2))) return true;
     }
     return false;
    }
    public int hashCode() {
      return (key==NULL_KEY ? 0 : key.hashCode()) ^ (value==null ? 0 : value.hashCode());
    }
    public String toString() {
      return getKey() + "=" + getValue();
    }
    void recordAccess(HashMap m) { }
    void recordRemoval(HashMap m) { }
}
一个静态内部类

void addEntry(int hash, Object key, Object value, int bucketIndex) {
    table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);
    if (size++ >= threshold)
      resize(2 * table.length);
}
注意这个方法,插入连表的头。
可以写成这样更好理解:
Entry oldHead=table[bucketIndex];
Entry newHead = new Entry(hash,key,value,oldHead);
table[bucketIndex]=newHead;

void createEntry(int hash, Object key, Object value, int bucketIndex) {
   table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);
   size++;
}

private abstract class HashIterator implements Iterator {
   Entry next;
   int expectedModCount;
   int index;
   Entry current;
   HashIterator() {
     expectedModCount = modCount;
     Entry[] t = table;
     int i = t.length;
     Entry n = null;
     if (size != 0) {
       while (i > 0 && (n = t[--i]) == null) ;
     }
     next = n;
     index = i;
   }
   public boolean hasNext() {
     return next != null;
   }
   Entry nextEntry() {
     if (modCount != expectedModCount)
       throw new ConcurrentModificationException();
     Entry e = next;
    if (e == null)
       throw new NoSuchElementException();
     Entry n = e.next;
     Entry[] t = table;
     int i = index;
     while (n == null && i > 0)
        n = t[--i]; index = i;
     next = n;
     return current = e;
   }
   public void remove() {
     if (current == null)
       throw new IllegalStateException();
     if (modCount != expectedModCount)
       throw new ConcurrentModificationException();
     Object k = current.key;
     current = null;
     HashMap.this.removeEntryForKey(k);
     expectedModCount = modCount;
   }
}

private class ValueIterator extends HashIterator {
   public Object next() {
     return nextEntry().value;
   }
}

private class KeyIterator extends HashIterator {
   public Object next() {
     return nextEntry().getKey();
   }
}

private class EntryIterator extends HashIterator {
   public Object next() {
     return nextEntry();
   }
}

Iterator newKeyIterator() {
   return new KeyIterator();
}

Iterator newValueIterator() {
   return new ValueIterator();
}

Iterator newEntryIterator() {  
   return new EntryIterator();
}

private transient Set entrySet = null;

public Set keySet() {
   Set ks = keySet;
   return (ks != null ? ks : (keySet = new KeySet()));
}

private class KeySet extends AbstractSet {
   public Iterator iterator() {
     return newKeyIterator();
   }
   public int size() {
     return size;
   }
   public boolean contains(Object o) {
     return containsKey(o);
   }
   public boolean remove(Object o) {
     return HashMap.this.removeEntryForKey(o) != null;
   }
   public void clear() {
     HashMap.this.clear();
   }
}

public Collection values() {
   Collection vs = values; return (vs != null ? vs : (values = new Values()));
}

private class Values extends AbstractCollection {
   public Iterator iterator() {
     return newValueIterator();
   }
   public int size() {
     return size;
   }
   public boolean contains(Object o) {
     return containsValue(o);
   }
   public void clear() {
     HashMap.this.clear();
   }
}

public Set entrySet() {
   Set es = entrySet;
   return (es != null ? es : (entrySet = new EntrySet()));
}

private class EntrySet extends AbstractSet {
   public Iterator iterator() {
     return newEntryIterator();
   }
   public boolean contains(Object o) {
     if (!(o instanceof Map.Entry))
       return false;
     Map.Entry e = (Map.Entry)o;
     Entry candidate = getEntry(e.getKey());
     return candidate != null && candidate.equals(e);
   }
   public boolean remove(Object o) {
     return removeMapping(o) != null;
   }
   public int size() {
     return size;
   }
   public void clear() {
     HashMap.this.clear();
   }
}

private void writeObject(java.io.ObjectOutputStream s) throws IOException {
   s.defaultWriteObject();
   s.writeInt(table.length);
   s.writeInt(size);
   for (Iterator i = entrySet().iterator(); i.hasNext(); ) {
     Map.Entry e = (Map.Entry) i.next();
     s.writeObject(e.getKey());
     s.writeObject(e.getValue());
   }
}

private static final long serialVersionUID = 362498820763181265L;

private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
   s.defaultReadObject();
   int numBuckets = s.readInt();
   table = new Entry[numBuckets];
   init();
   size = s.readInt(); for (int i=0;
   for (int i=0; i<size; i++) {
     Object key = s.readObject();
     Object value = s.readObject(); 
     putForCreate(key, value);
   }
}

int capacity() {
  return table.length;
}
float loadFactor() {
   return loadFactor;
}

HashMap是Java新Collection Framework中用来代替HashTable的一个实现,HashMap和HashTable的区别是: HashMap是未经同步的,而且允许null值。HashTable继承Dictionary,而且使用了Enumeration,所以被建议不要使用。
HashMap的声明如下:
public class HashMap extends AbstractMap implements Map, Cloneable,Serializable
有关AbstractMap:http://blog.youkuaiyun.com/treeroot/archive/2004/09/20/110343.aspx
有关Map:http://blog.youkuaiyun.com/treeroot/archive/2004/09/20/110331.aspx
有关Cloneable:http://blog.youkuaiyun.com/treeroot/archive/2004/09/07/96936.aspx
这个类比较复杂,这里只是重点分析了几个方法,特别是后面涉及到很多内部类都没有解释
不过都比较简单。

static final int DEFAULT_INITIAL_CAPACITY = 16; 默认初始化大小

static final int MAXIMUM_CAPACITY = 1 << 30; 最大初始化大小

static final float DEFAULT_LOAD_FACTOR = 0.75f; 默认加载因子

transient Entry[] table; 一个Entry类型的数组,数组的长度为2的指数。

transient int size; 映射的个数

int threshold; 下一次扩容时的值

final float loadFactor; 加载因子

transient volatile int modCount; 修改次数

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);
   int capacity = 1;
   while (capacity < initialCapacity)
     capacity <<= 1;
   this.loadFactor = loadFactor;
   threshold = (int)(capacity * loadFactor);
   table = new Entry[capacity];
   init();
}

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

public HashMap() {
   this.loadFactor = DEFAULT_LOAD_FACTOR;
   threshold = (int)(DEFAULT_INITIAL_CAPACITY);
      注意:这里应该是一个失误! 应该是:threshold =(int)(DEFAULT_INITIAL_CAPACITY * loadFactor);
   table = new Entry[DEFAULT_INITIAL_CAPACITY];
   init();
}

public HashMap(Map m) {
   this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY),                                   DEFAULT_LOAD_FACTOR);
   putAllForCreate(m);
}

void init() {}

static final Object NULL_KEY = new Object();

static Object maskNull(Object key){
   return (key == null ? NULL_KEY : key);
}

static Object unmaskNull(Object key) {
   return (key == NULL_KEY ? null : key);
}

static int hash(Object x) {
   int h = x.hashCode();
   h += ~(h << 9);
   h ^= (h >>> 14);
   h += (h << 4);
   h ^= (h >>> 10);
return h;
}
在HashTable中没有这个方法,也就是说HashTable中是直接用对象的hashCode值,但是HashMap做了改进 用这个算法来获得哈希值。

static boolean eq(Object x, Object y) {
   return x == y || x.equals(y);
}

static int indexFor(int h, int length) {
   return h & (length-1);
}
根据哈希值和数组的长度来返回该hash值在数组中的位置,只是简单的与关系。

public int size() {
   return size;
}

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

public Object get(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry e = table[i];
   while (true) {
     if (e == null) return e;
     if (e.hash == hash && eq(k, e.key)) return e.value;
     e = e.next;
   }
}
这个方法是获取数据的方法,首先获得哈希值,这里把null值掩饰了,并且hash值经过函数hash()修正。 然后计算该哈希值在数组中的索引值。如果该索引处的引用为null,表示HashMap中不存在这个映射。 否则的话遍历整个链表,这里找到了就返回,如果没有找到就遍历到链表末尾,返回null。这里的比较是这样的:e.hash==hash && eq(k,e.key) 也就是说如果hash不同就肯定认为不相等,eq就被短路了,只有在 hash相同的情况下才调用equals方法。现在我们该明白Object中说的如果两个对象equals返回true,他们的 hashCode应该相同的道理了吧。假如两个对象调用equals返回true,但是hashCode不一样,那么在HashMap 里就认为他们不相等。

public boolean containsKey(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry e = table[i];
   while (e != null) {
     if (e.hash == hash && eq(k, e.key)) return true;
     e = e.next;
   }
  return false;
}
这个方法比上面的简单,先找到哈希位置,再遍历整个链表,如果找到就返回true。

Entry getEntry(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry e = table[i];
   while (e != null && !(e.hash == hash && eq(k, e.key)))
     e = e.next;
   return e;
}
这个方法根据key值返回Entry节点,也是先获得索引位置,再遍历链表,如果没有找到返回的是null。

public Object put(Object key, Object value) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   for (Entry e = table[i]; e != null; e = e.next) {
     if (e.hash == hash && eq(k, e.key)) {
       Object oldValue = e.value;
       e.value = value;
       e.recordAccess(this);
       return oldValue;
     }
   }
   modCount++;
   addEntry(hash, k, value, i);
   return null;
}
首先获得hash索引位置,如果该位置的引用为null,那么直接插入一个映射,返回null。如果此处的引用不是null,必须遍历链表,如果找到一个相同的key,那么就更新该value,同时返回原来的value值。如果遍历完了没有找到,说明该key值不存在,还是插入一个映射。如果hash值足够离散的话,也就是说该索引没有被使用的话,那么不不用遍历链表了。相反,如果hash值不离散,极端的说如果是常数的话,所有的映射都会在这一个链表上,效率会极其低下。这里举一个最简单的例子,写两
个不同的类作为key插入到HashMap中,效率会远远不同。
class Good{
  int i;
  public Good(int i){
   this.i=i;
  }
  public boolean equals(Object o){
   return (o instanceof Good) && (this.i==((Good)o).i)
  }
  public int hashCode(){
   return i;
  }
}
class Bad{
  int i;
  public Good(int i){
    this.i=i;
  }
  public boolean equals(Object o){
    return (o instanceof Good) && (this.i==((Good)o).i)
  }
  public int hashCode(){
   return 0;
  }
}
执行代码:
Map m1=new HashMap();
Map m2=new HashMap();
for(int i=0;i<100;i++){
  m1.put(new Good(i),new Integer(i)); //这里效率非常高
}
for(int i=0;i<100;i++){
  m2.put(new Bad(i),new Integer(i)); //这里几乎要崩溃
}
上面的是两个非常极端的例子,执行一下就知道差别有多大。

private void putForCreate(Object key, Object value) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   for (Entry e = table[i]; e != null; e = e.next) {
     if (e.hash == hash && eq(k, e.key)) {
        e.value = value;
        return;
     }
   }
   createEntry(hash, k, value, i);
}

void putAllForCreate(Map m) {
   for (Iterator i = m.entrySet().iterator(); i.hasNext(); ) {
     Map.Entry e = (Map.Entry) i.next();
     putForCreate(e.getKey(), e.getValue());
   }
}
上面的两个方法是被构造函数和clone方法调用的。

void resize(int newCapacity) {
   Entry[] oldTable = table;
   int oldCapacity = oldTable.length;
   if (size < threshold || oldCapacity > newCapacity)
     return;
   Entry[] newTable = new Entry[newCapacity];
   transfer(newTable);
   table = newTable;
   threshold = (int)(newCapacity * loadFactor);
}
这个方法在需要的时候重新分配空间,相当于ArrayList的ensureCapacity方法,不过这个更加复杂。

void transfer(Entry[] newTable) {
   Entry[] src = table;
   int newCapacity = newTable.length;
   for (int j = 0; j < src.length; j++) {
     Entry e = src[j];
     if (e != null) {
       src[j] = null;
       do {
          Entry next = e.next;
          int i = indexFor(e.hash, newCapacity);
          e.next = newTable[i];
          newTable[i] = e;
          e = next;
       } while (e != null);
     }
   }
}
遍历原来的数组,如果该Entry不是null的话,说明有映射,然后遍历这个链表,把所有的映射插入到新的数组中,注意这里要从新计算索引位置。

public void putAll(Map t) {
   int n = t.size();
   if (n == 0)
     return;
   if (n >= threshold) {
     n = (int)(n / loadFactor + 1);
     if (n > MAXIMUM_CAPACITY)
       n = MAXIMUM_CAPACITY;
     int capacity = table.length;
     while (capacity < n) capacity <<= 1;
       resize(capacity);
   }
   for (Iterator i = t.entrySet().iterator(); i.hasNext(); ) {
     Map.Entry e = (Map.Entry) i.next();
     put(e.getKey(), e.getValue());
   }
}
这个方法先确定是否需要扩大空间,然后循环调用put方法。

public Object remove(Object key) {
   Entry e = removeEntryForKey(key);
   return (e == null ? e : e.value);
}

Entry removeEntryForKey(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry prev = table[i];
   Entry e = prev;
   while (e != null) {  如果e==null表示不存在
     Entry next = e.next;
     if (e.hash == hash && eq(k, e.key)) {
       modCount++;
       size--;
       if (prev == e)
         table[i] = next; 链表的第一个元素就是要删除的,这里最好加一句 e.next=null.
       else
         prev.next = next; 存在担不是链表的第一个元素, 这里最好加一句 e.next=null.
       e.recordRemoval(this);
       return e;
     }
     prev = e;
     e = next;
   }
  return e;   这里其实就是return null;
}
这个方法其实也不复杂,也是遍历链表,这里建议加一句e.next=null,可以改为
if(prev==e)
  table[i]=next;
else
  prev.next=next;
e.next=null; 这一句是多加的,可以提高效率。
这里简单说明我的看法:
因为e是被删除的节点,删除它其实就是指向它的指针指向它的后面一个节点。所以e可以作为GC回收的对象。
可以e还有一个next指针指向我们的数据,如果e没有被回收。而且此时e.next指向的节点也变为没用的了,但是
却有一个它的引用(e.next),所以虽然e的下一个节点没用了,但是却不能作为GC回收的对象,除非e先被回收。
虽然不一定会引起很大的问题,但是至少会影响GC的回收效率。就像数据库中的外键引用一样,删除起来很麻烦呀。

Entry removeMapping(Object o) {
   if (!(o instanceof Map.Entry))
     return null;
   Map.Entry entry = (Map.Entry)o;
   Object k = maskNull(entry.getKey());
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry prev = table[i];
   Entry e = prev;
   while (e != null) {
     Entry next = e.next;
     if (e.hash == hash && e.equals(entry)) {
       modCount++;
       size--;
       if (prev == e)
         table[i] = next;
       else
         prev.next = next;
       e.recordRemoval(this);
       return e;
      }
      prev = e;
      e = next;
   }
   return e;
}
这个方法和上面的一样。

public void clear() {
   modCount++;
   Entry tab[] = table;
   for (int i = 0; i < tab.length; i++)
     tab[i] = null;
   size = 0;
}
同样可以改进

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;
}

private boolean containsNullValue() {
   Entry tab[] = table;
   for (int i = 0; i < tab.length ; i++)
     for (Entry e = tab[i] ; e != null ; e = e.next)
       if (e.value == null) return true;
   return false;
}

public Object clone() {
   HashMap result = null;
   try {
     result = (HashMap)super.clone();
   }
   catch (CloneNotSupportedException e) { // assert false; }
   result.table = new Entry[table.length];
   result.entrySet = null;
   result.modCount = 0;
   result.size = 0;
   result.init();
   result.putAllForCreate(this);
   return result;
}

static class Entry implements Map.Entry {
   final Object key;
   Object value;
   final int hash;
   Entry next;
   Entry(int h, Object k, Object v, Entry n) {
     value = v;
     next = n;
     key = k;
     hash = h;
   }
   public Object getKey() {
     return unmaskNull(key);
   }
   public Object getValue() {
     return value;
   }
   public Object setValue(Object newValue) {
      Object oldValue = value;
      value = newValue;
      return oldValue;
   }
   public boolean equals(Object o) {
     if (!(o instanceof Map.Entry)) return false;
     Map.Entry e = (Map.Entry)o;
     Object k1 = getKey();
     Object k2 = e.getKey();
     if (k1 == k2 || (k1 != null && k1.equals(k2))) {
       Object v1 = getValue();
       Object v2 = e.getValue();
       if (v1 == v2 || (v1 != null && v1.equals(v2))) return true;
     }
     return false;
    }
    public int hashCode() {
      return (key==NULL_KEY ? 0 : key.hashCode()) ^ (value==null ? 0 : value.hashCode());
    }
    public String toString() {
      return getKey() + "=" + getValue();
    }
    void recordAccess(HashMap m) { }
    void recordRemoval(HashMap m) { }
}
一个静态内部类

void addEntry(int hash, Object key, Object value, int bucketIndex) {
    table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);
    if (size++ >= threshold)
      resize(2 * table.length);
}
注意这个方法,插入连表的头。
可以写成这样更好理解:
Entry oldHead=table[bucketIndex];
Entry newHead = new Entry(hash,key,value,oldHead);
table[bucketIndex]=newHead;

void createEntry(int hash, Object key, Object value, int bucketIndex) {
   table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);
   size++;
}

private abstract class HashIterator implements Iterator {
   Entry next;
   int expectedModCount;
   int index;
   Entry current;
   HashIterator() {
     expectedModCount = modCount;
     Entry[] t = table;
     int i = t.length;
     Entry n = null;
     if (size != 0) {
       while (i > 0 && (n = t[--i]) == null) ;
     }
     next = n;
     index = i;
   }
   public boolean hasNext() {
     return next != null;
   }
   Entry nextEntry() {
     if (modCount != expectedModCount)
       throw new ConcurrentModificationException();
     Entry e = next;
    if (e == null)
       throw new NoSuchElementException();
     Entry n = e.next;
     Entry[] t = table;
     int i = index;
     while (n == null && i > 0)
        n = t[--i]; index = i;
     next = n;
     return current = e;
   }
   public void remove() {
     if (current == null)
       throw new IllegalStateException();
     if (modCount != expectedModCount)
       throw new ConcurrentModificationException();
     Object k = current.key;
     current = null;
     HashMap.this.removeEntryForKey(k);
     expectedModCount = modCount;
   }
}

private class ValueIterator extends HashIterator {
   public Object next() {
     return nextEntry().value;
   }
}

private class KeyIterator extends HashIterator {
   public Object next() {
     return nextEntry().getKey();
   }
}

private class EntryIterator extends HashIterator {
   public Object next() {
     return nextEntry();
   }
}

Iterator newKeyIterator() {
   return new KeyIterator();
}

Iterator newValueIterator() {
   return new ValueIterator();
}

Iterator newEntryIterator() {  
   return new EntryIterator();
}

private transient Set entrySet = null;

public Set keySet() {
   Set ks = keySet;
   return (ks != null ? ks : (keySet = new KeySet()));
}

private class KeySet extends AbstractSet {
   public Iterator iterator() {
     return newKeyIterator();
   }
   public int size() {
     return size;
   }
   public boolean contains(Object o) {
     return containsKey(o);
   }
   public boolean remove(Object o) {
     return HashMap.this.removeEntryForKey(o) != null;
   }
   public void clear() {
     HashMap.this.clear();
   }
}

public Collection values() {
   Collection vs = values; return (vs != null ? vs : (values = new Values()));
}

private class Values extends AbstractCollection {
   public Iterator iterator() {
     return newValueIterator();
   }
   public int size() {
     return size;
   }
   public boolean contains(Object o) {
     return containsValue(o);
   }
   public void clear() {
     HashMap.this.clear();
   }
}

public Set entrySet() {
   Set es = entrySet;
   return (es != null ? es : (entrySet = new EntrySet()));
}

private class EntrySet extends AbstractSet {
   public Iterator iterator() {
     return newEntryIterator();
   }
   public boolean contains(Object o) {
     if (!(o instanceof Map.Entry))
       return false;
     Map.Entry e = (Map.Entry)o;
     Entry candidate = getEntry(e.getKey());
     return candidate != null && candidate.equals(e);
   }
   public boolean remove(Object o) {
     return removeMapping(o) != null;
   }
   public int size() {
     return size;
   }
   public void clear() {
     HashMap.this.clear();
   }
}

private void writeObject(java.io.ObjectOutputStream s) throws IOException {
   s.defaultWriteObject();
   s.writeInt(table.length);
   s.writeInt(size);
   for (Iterator i = entrySet().iterator(); i.hasNext(); ) {
     Map.Entry e = (Map.Entry) i.next();
     s.writeObject(e.getKey());
     s.writeObject(e.getValue());
   }
}

private static final long serialVersionUID = 362498820763181265L;

private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
   s.defaultReadObject();
   int numBuckets = s.readInt();
   table = new Entry[numBuckets];
   init();
   size = s.readInt(); for (int i=0;
   for (int i=0; i<size; i++) {
     Object key = s.readObject();
     Object value = s.readObject(); 
     putForCreate(key, value);
   }
}

int capacity() {
  return table.length;
}
float loadFactor() {
   return loadFactor;
}

Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMap

Collection接口
  Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。一些Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。
  所有实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数用于创建一个空的Collection,有一个Collection参数的构造函数用于创建一个新的Collection,这个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection。
  如何遍历Collection中的每一个元素?不论Collection的实际类型如何,它都支持一个iterator()的方法,该方法返回一个迭代子,使用该迭代子即可逐一访问Collection中每一个元素。典型的用法如下:
    Iterator it = collection.iterator(); // 获得一个迭代子
    while(it.hasNext()) {
      Object obj = it.next(); // 得到下一个元素
    }

  由Collection接口派生的两个接口是List和Set。

List接口
  List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。
和下面要提到的Set不同,List允许有相同的元素。
  除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素,还能向前或向后遍历。
  实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。

LinkedList类
  LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法在LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。
  注意LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:
    List list = Collections.synchronizedList(new LinkedList(...));

ArrayList类
  ArrayList实现了可变大小的数组。它允许所有元素,包括null。ArrayList没有同步。
size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。
  每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。
  和LinkedList一样,ArrayList也是非同步的(unsynchronized)。

Vector类
  Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和ArrayList创建的Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出ConcurrentModificationException,因此必须捕获该异常。

Stack 类
  Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。

Set接口
  Set是一种不包含重复的元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。
  很明显,Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。
  请注意:必须小心操作可变对象(Mutable Object)。如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。

Map接口
  请注意,Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个value。Map接口提供3种集合的视图,Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。

Hashtable类
  Hashtable继承Map接口,实现一个key-value映射的哈希表。任何非空(non-null)的对象都可作为key或者value。
  添加数据使用put(key, value),取出数据使用get(key),这两个基本操作的时间开销为常数。
Hashtable通过initial capacity和load factor两个参数调整性能。通常缺省的load factor 0.75较好地实现了时间和空间的均衡。增大load factor可以节省空间但相应的查找时间将增大,这会影响像get和put这样的操作。
使用Hashtable的简单示例如下,将1,2,3放到Hashtable中,他们的key分别是”one”,”two”,”three”:
    Hashtable numbers = new Hashtable();
    numbers.put(“one”, new Integer(1));
    numbers.put(“two”, new Integer(2));
    numbers.put(“three”, new Integer(3));

  要取出一个数,比如2,用相应的key:
    Integer n = (Integer)numbers.get(“two”);
    System.out.println(“two = ” + n);

  由于作为key的对象将通过计算其散列函数来确定与之对应的value的位置,因此任何作为key的对象都必须实现hashCode和equals方法。hashCode和equals方法继承自根类Object,如果你用自定义的类当作key的话,要相当小心,按照散列函数的定义,如果两个对象相同,即obj1.equals(obj2)=true,则它们的hashCode必须相同,但如果两个对象不同,则它们的hashCode不一定不同,如果两个不同对象的hashCode相同,这种现象称为冲突,冲突会导致操作哈希表的时间开销增大,所以尽量定义好的hashCode()方法,能加快哈希表的操作。
  如果相同的对象有不同的hashCode,对哈希表的操作会出现意想不到的结果(期待的get方法返回null),要避免这种问题,只需要牢记一条:要同时复写equals方法和hashCode方法,而不要只写其中一个。
  Hashtable是同步的。

HashMap类
  HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null value和null key。,但是将HashMap视为Collection时(values()方法可返回Collection),其迭代子操作时间开销和HashMap的容量成比例。因此,如果迭代操作的性能相当重要的话,不要将HashMap的初始化容量设得过高,或者load factor过低。

WeakHashMap类
  WeakHashMap是一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收。

总结
  如果涉及到堆栈,队列等操作,应该考虑用List,对于需要快速插入,删除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList。
  如果程序在单线程环境中,或者访问仅仅在一个线程中进行,考虑非同步的类,其效率较高,如果多个线程可能同时操作一个类,应该使用同步的类。
  要特别注意对哈希表的操作,作为key的对象要正确复写equals和hashCode方法。
  尽量返回接口而非实际的类型,如返回List而非ArrayList,这样如果以后需要将ArrayList换成LinkedList时,客户端代码不用改变。这就是针对抽象编程。

Java中对HashMap的深度分析与比较在Java的世界里,无论类还是各种数据,其结构的处理是整个程序的逻辑以及性能的关键。由于本人接触了一个有关性能与逻辑同时并存的问题,于是就开始研究这方面的问题。找遍了大大小小的论坛,也把《Java 虚拟机规范》,《apress,.java.collections.(2001),.bm.ocr.6.0.shareconnector》,和《Thinking in Java》翻了也找不到很好的答案,于是一气之下把JDK的 src 解压出来研究,扩然开朗,遂写此文,跟大家分享感受和顺便验证我理解还有没有漏洞。 这里就拿HashMap来研究吧。

  HashMap可谓JDK的一大实用工具,把各个Object映射起来,实现了“键--值”对应的快速存取。但实际里面做了些什么呢?

  在这之前,先介绍一下负载因子和容量的属性。大家都知道其实一个 HashMap 的实际容量就 因子*容量,其默认值是 16×0.75=12; 这个很重要,对效率很一定影响!当存入HashMap的对象超过这个容量时,HashMap 就会重新构造存取表。这就是一个大问题,我后面慢慢介绍,反正,如果你已经知道你大概要存放多少个对象,最好设为该实际容量的能接受的数字。

  两个关键的方法,put和get:

  先有这样一个概念,HashMap是声明了 Map,Cloneable, Serializable 接口,和继承了 AbstractMap 类,里面的 Iterator 其实主要都是其内部类HashIterator 和其他几个 iterator 类实现,当然还有一个很重要的继承了Map.Entry 的 Entry 内部类,由于大家都有源代码,大家有兴趣可以看看这部分,我主要想说明的是 Entry 内部类。它包含了hash,value,key 和next 这四个属性,很重要。put的源码如下

public Object put(Object key, Object value) {
Object k = maskNull(key);

  这个就是判断键值是否为空,并不很深奥,其实如果为空,它会返回一个static Object 作为键值,这就是为什么HashMap允许空键值的原因。

int hash = hash(k);
int i = indexFor(hash, table.length);

  这连续的两步就是 HashMap 最牛的地方!研究完我都汗颜了,其中 hash 就是通过 key 这个Object的 hashcode 进行 hash,然后通过 indexFor 获得在Object table的索引值。

  table???不要惊讶,其实HashMap也神不到哪里去,它就是用 table 来放的。最牛的就是用 hash 能正确的返回索引。其中的hash算法,我跟JDK的作者 Doug 联系过,他建议我看看《The art of programing vol3》可恨的是,我之前就一直在找,我都找不到,他这样一提,我就更加急了,可惜口袋空空啊!!!

  不知道大家有没有留意 put 其实是一个有返回的方法,它会把相同键值的 put 覆盖掉并返回旧的值!如下方法彻底说明了 HashMap 的结构,其实就是一个表加上在相应位置的Entry的链表:

for (Entry e = table[i]; e != null; e = e.next) {
 if (e.hash == hash && eq(k, e.key)) {
  Object oldvalue = e.value;
  e.value = value; //把新的值赋予给对应键值。
  e.recordAccess(this); //空方法,留待实现
  return oldvalue; //返回相同键值的对应的旧的值。
 }
}
modCount++; //结构性更改的次数
addEntry(hash, k, value, i); //添加新元素,关键所在!
return null; //没有相同的键值返回
}

  我们把关键的方法拿出来分析:

void addEntry(int hash, Object key, Object value, int bucketIndex) {
table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);

  因为 hash 的算法有可能令不同的键值有相同的hash码并有相同的table索引,如:key=“33”和key=Object g的hash都是-8901334,那它经过indexfor之后的索引一定都为i,这样在new的时候这个Entry的next就会指向这个原本的table[i],再有下一个也如此,形成一个链表,和put的循环对定e.next获得旧的值。到这里,HashMap的结构,大家也十分明白了吧?

if (size++ >= threshold) //这个threshold就是能实际容纳的量
resize(2 * table.length); //超出这个容量就会将Object table重构

  所谓的重构也不神,就是建一个两倍大的table(我在别的论坛上看到有人说是两倍加1,把我骗了),然后再一个个indexfor进去!注意!!这就是效率!!如果你能让你的HashMap不需要重构那么多次,效率会大大提高!

  说到这里也差不多了,get比put简单得多,大家,了解put,get也差不了多少了。对于collections我是认为,它是适合广泛的,当不完全适合特有的,如果大家的程序需要特殊的用途,自己写吧,其实很简单。(作者是这样跟我说的,他还建议我用LinkedHashMap,我看了源码以后发现,LinkHashMap其实就是继承HashMap的,然后override相应的方法,有兴趣的同人,自己looklook)建个 Object table,写相应的算法,就ok啦。

  举个例子吧,像 Vector,list 啊什么的其实都很简单,最多就多了的同步的声明,其实如果要实现像Vector那种,插入,删除不多的,可以用一个Object table来实现,按索引存取,添加等。

  如果插入,删除比较多的,可以建两个Object table,然后每个元素用含有next结构的,一个table存,如果要插入到i,但是i已经有元素,用next连起来,然后size++,并在另一个table记录其位置。
源码分析:HashMap
     

HashMap是Java新Collection Framework中用来代替HashTable的一个实现,HashMap和HashTable的区别是: HashMap是未经同步的,而且允许null值。HashTable继承Dictionary,而且使用了Enumeration,所以被建议不要使用。
HashMap的声明如下:
public class HashMap extends AbstractMap implements Map, Cloneable,Serializable
有关AbstractMap:http://blog.youkuaiyun.com/treeroot/archive/2004/09/20/110343.aspx
有关Map:http://blog.youkuaiyun.com/treeroot/archive/2004/09/20/110331.aspx
有关Cloneable:http://blog.youkuaiyun.com/treeroot/archive/2004/09/07/96936.aspx
这个类比较复杂,这里只是重点分析了几个方法,特别是后面涉及到很多内部类都没有解释
不过都比较简单。

static final int DEFAULT_INITIAL_CAPACITY = 16; 默认初始化大小

static final int MAXIMUM_CAPACITY = 1 << 30; 最大初始化大小

static final float DEFAULT_LOAD_FACTOR = 0.75f; 默认加载因子

transient Entry[] table; 一个Entry类型的数组,数组的长度为2的指数。

transient int size; 映射的个数

int threshold; 下一次扩容时的值

final float loadFactor; 加载因子

transient volatile int modCount; 修改次数

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);
   int capacity = 1;
   while (capacity < initialCapacity)
     capacity <<= 1;
   this.loadFactor = loadFactor;
   threshold = (int)(capacity * loadFactor);
   table = new Entry[capacity];
   init();
}

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

public HashMap() {
   this.loadFactor = DEFAULT_LOAD_FACTOR;
   threshold = (int)(DEFAULT_INITIAL_CAPACITY);
      注意:这里应该是一个失误! 应该是:threshold =(int)(DEFAULT_INITIAL_CAPACITY * loadFactor);
   table = new Entry[DEFAULT_INITIAL_CAPACITY];
   init();
}

public HashMap(Map m) {
   this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY),                                   DEFAULT_LOAD_FACTOR);
   putAllForCreate(m);
}

void init() {}

static final Object NULL_KEY = new Object();

static Object maskNull(Object key){
   return (key == null ? NULL_KEY : key);
}

static Object unmaskNull(Object key) {
   return (key == NULL_KEY ? null : key);
}

static int hash(Object x) {
   int h = x.hashCode();
   h += ~(h << 9);
   h ^= (h >>> 14);
   h += (h << 4);
   h ^= (h >>> 10);
return h;
}
在HashTable中没有这个方法,也就是说HashTable中是直接用对象的hashCode值,但是HashMap做了改进 用这个算法来获得哈希值。

static boolean eq(Object x, Object y) {
   return x == y || x.equals(y);
}

static int indexFor(int h, int length) {
   return h & (length-1);
}
根据哈希值和数组的长度来返回该hash值在数组中的位置,只是简单的与关系。

public int size() {
   return size;
}

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

public Object get(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry e = table[i];
   while (true) {
     if (e == null) return e;
     if (e.hash == hash && eq(k, e.key)) return e.value;
     e = e.next;
   }
}
这个方法是获取数据的方法,首先获得哈希值,这里把null值掩饰了,并且hash值经过函数hash()修正。 然后计算该哈希值在数组中的索引值。如果该索引处的引用为null,表示HashMap中不存在这个映射。 否则的话遍历整个链表,这里找到了就返回,如果没有找到就遍历到链表末尾,返回null。这里的比较是这样的:e.hash==hash && eq(k,e.key) 也就是说如果hash不同就肯定认为不相等,eq就被短路了,只有在 hash相同的情况下才调用equals方法。现在我们该明白Object中说的如果两个对象equals返回true,他们的 hashCode应该相同的道理了吧。假如两个对象调用equals返回true,但是hashCode不一样,那么在HashMap 里就认为他们不相等。

public boolean containsKey(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry e = table[i];
   while (e != null) {
     if (e.hash == hash && eq(k, e.key)) return true;
     e = e.next;
   }
  return false;
}
这个方法比上面的简单,先找到哈希位置,再遍历整个链表,如果找到就返回true。

Entry getEntry(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry e = table[i];
   while (e != null && !(e.hash == hash && eq(k, e.key)))
     e = e.next;
   return e;
}
这个方法根据key值返回Entry节点,也是先获得索引位置,再遍历链表,如果没有找到返回的是null。

public Object put(Object key, Object value) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   for (Entry e = table[i]; e != null; e = e.next) {
     if (e.hash == hash && eq(k, e.key)) {
       Object oldValue = e.value;
       e.value = value;
       e.recordAccess(this);
       return oldValue;
     }
   }
   modCount++;
   addEntry(hash, k, value, i);
   return null;
}
首先获得hash索引位置,如果该位置的引用为null,那么直接插入一个映射,返回null。如果此处的引用不是null,必须遍历链表,如果找到一个相同的key,那么就更新该value,同时返回原来的value值。如果遍历完了没有找到,说明该key值不存在,还是插入一个映射。如果hash值足够离散的话,也就是说该索引没有被使用的话,那么不不用遍历链表了。相反,如果hash值不离散,极端的说如果是常数的话,所有的映射都会在这一个链表上,效率会极其低下。这里举一个最简单的例子,写两
个不同的类作为key插入到HashMap中,效率会远远不同。
class Good{
  int i;
  public Good(int i){
   this.i=i;
  }
  public boolean equals(Object o){
   return (o instanceof Good) && (this.i==((Good)o).i)
  }
  public int hashCode(){
   return i;
  }
}
class Bad{
  int i;
  public Good(int i){
    this.i=i;
  }
  public boolean equals(Object o){
    return (o instanceof Good) && (this.i==((Good)o).i)
  }
  public int hashCode(){
   return 0;
  }
}
执行代码:
Map m1=new HashMap();
Map m2=new HashMap();
for(int i=0;i<100;i++){
  m1.put(new Good(i),new Integer(i)); //这里效率非常高
}
for(int i=0;i<100;i++){
  m2.put(new Bad(i),new Integer(i)); //这里几乎要崩溃
}
上面的是两个非常极端的例子,执行一下就知道差别有多大。

private void putForCreate(Object key, Object value) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   for (Entry e = table[i]; e != null; e = e.next) {
     if (e.hash == hash && eq(k, e.key)) {
        e.value = value;
        return;
     }
   }
   createEntry(hash, k, value, i);
}

void putAllForCreate(Map m) {
   for (Iterator i = m.entrySet().iterator(); i.hasNext(); ) {
     Map.Entry e = (Map.Entry) i.next();
     putForCreate(e.getKey(), e.getValue());
   }
}
上面的两个方法是被构造函数和clone方法调用的。

void resize(int newCapacity) {
   Entry[] oldTable = table;
   int oldCapacity = oldTable.length;
   if (size < threshold || oldCapacity > newCapacity)
     return;
   Entry[] newTable = new Entry[newCapacity];
   transfer(newTable);
   table = newTable;
   threshold = (int)(newCapacity * loadFactor);
}
这个方法在需要的时候重新分配空间,相当于ArrayList的ensureCapacity方法,不过这个更加复杂。

void transfer(Entry[] newTable) {
   Entry[] src = table;
   int newCapacity = newTable.length;
   for (int j = 0; j < src.length; j++) {
     Entry e = src[j];
     if (e != null) {
       src[j] = null;
       do {
          Entry next = e.next;
          int i = indexFor(e.hash, newCapacity);
          e.next = newTable[i];
          newTable[i] = e;
          e = next;
       } while (e != null);
     }
   }
}
遍历原来的数组,如果该Entry不是null的话,说明有映射,然后遍历这个链表,把所有的映射插入到新的数组中,注意这里要从新计算索引位置。

public void putAll(Map t) {
   int n = t.size();
   if (n == 0)
     return;
   if (n >= threshold) {
     n = (int)(n / loadFactor + 1);
     if (n > MAXIMUM_CAPACITY)
       n = MAXIMUM_CAPACITY;
     int capacity = table.length;
     while (capacity < n) capacity <<= 1;
       resize(capacity);
   }
   for (Iterator i = t.entrySet().iterator(); i.hasNext(); ) {
     Map.Entry e = (Map.Entry) i.next();
     put(e.getKey(), e.getValue());
   }
}
这个方法先确定是否需要扩大空间,然后循环调用put方法。

public Object remove(Object key) {
   Entry e = removeEntryForKey(key);
   return (e == null ? e : e.value);
}

Entry removeEntryForKey(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry prev = table[i];
   Entry e = prev;
   while (e != null) {  如果e==null表示不存在
     Entry next = e.next;
     if (e.hash == hash && eq(k, e.key)) {
       modCount++;
       size--;
       if (prev == e)
         table[i] = next; 链表的第一个元素就是要删除的,这里最好加一句 e.next=null.
       else
         prev.next = next; 存在担不是链表的第一个元素, 这里最好加一句 e.next=null.
       e.recordRemoval(this);
       return e;
     }
     prev = e;
     e = next;
   }
  return e;   这里其实就是return null;
}
这个方法其实也不复杂,也是遍历链表,这里建议加一句e.next=null,可以改为
if(prev==e)
  table[i]=next;
else
  prev.next=next;
e.next=null; 这一句是多加的,可以提高效率。
这里简单说明我的看法:
因为e是被删除的节点,删除它其实就是指向它的指针指向它的后面一个节点。所以e可以作为GC回收的对象。
可以e还有一个next指针指向我们的数据,如果e没有被回收。而且此时e.next指向的节点也变为没用的了,但是
却有一个它的引用(e.next),所以虽然e的下一个节点没用了,但是却不能作为GC回收的对象,除非e先被回收。
虽然不一定会引起很大的问题,但是至少会影响GC的回收效率。就像数据库中的外键引用一样,删除起来很麻烦呀。

Entry removeMapping(Object o) {
   if (!(o instanceof Map.Entry))
     return null;
   Map.Entry entry = (Map.Entry)o;
   Object k = maskNull(entry.getKey());
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry prev = table[i];
   Entry e = prev;
   while (e != null) {
     Entry next = e.next;
     if (e.hash == hash && e.equals(entry)) {
       modCount++;
       size--;
       if (prev == e)
         table[i] = next;
       else
         prev.next = next;
       e.recordRemoval(this);
       return e;
      }
      prev = e;
      e = next;
   }
   return e;
}
这个方法和上面的一样。

public void clear() {
   modCount++;
   Entry tab[] = table;
   for (int i = 0; i < tab.length; i++)
     tab[i] = null;
   size = 0;
}
同样可以改进

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;
}

private boolean containsNullValue() {
   Entry tab[] = table;
   for (int i = 0; i < tab.length ; i++)
     for (Entry e = tab[i] ; e != null ; e = e.next)
       if (e.value == null) return true;
   return false;
}

public Object clone() {
   HashMap result = null;
   try {
     result = (HashMap)super.clone();
   }
   catch (CloneNotSupportedException e) { // assert false; }
   result.table = new Entry[table.length];
   result.entrySet = null;
   result.modCount = 0;
   result.size = 0;
   result.init();
   result.putAllForCreate(this);
   return result;
}

static class Entry implements Map.Entry {
   final Object key;
   Object value;
   final int hash;
   Entry next;
   Entry(int h, Object k, Object v, Entry n) {
     value = v;
     next = n;
     key = k;
     hash = h;
   }
   public Object getKey() {
     return unmaskNull(key);
   }
   public Object getValue() {
     return value;
   }
   public Object setValue(Object newValue) {
      Object oldValue = value;
      value = newValue;
      return oldValue;
   }
   public boolean equals(Object o) {
     if (!(o instanceof Map.Entry)) return false;
     Map.Entry e = (Map.Entry)o;
     Object k1 = getKey();
     Object k2 = e.getKey();
     if (k1 == k2 || (k1 != null && k1.equals(k2))) {
       Object v1 = getValue();
       Object v2 = e.getValue();
       if (v1 == v2 || (v1 != null && v1.equals(v2))) return true;
     }
     return false;
    }
    public int hashCode() {
      return (key==NULL_KEY ? 0 : key.hashCode()) ^ (value==null ? 0 : value.hashCode());
    }
    public String toString() {
      return getKey() + "=" + getValue();
    }
    void recordAccess(HashMap m) { }
    void recordRemoval(HashMap m) { }
}
一个静态内部类

void addEntry(int hash, Object key, Object value, int bucketIndex) {
    table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);
    if (size++ >= threshold)
      resize(2 * table.length);
}
注意这个方法,插入连表的头。
可以写成这样更好理解:
Entry oldHead=table[bucketIndex];
Entry newHead = new Entry(hash,key,value,oldHead);
table[bucketIndex]=newHead;

void createEntry(int hash, Object key, Object value, int bucketIndex) {
   table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);
   size++;
}

private abstract class HashIterator implements Iterator {
   Entry next;
   int expectedModCount;
   int index;
   Entry current;
   HashIterator() {
     expectedModCount = modCount;
     Entry[] t = table;
     int i = t.length;
     Entry n = null;
     if (size != 0) {
       while (i > 0 && (n = t[--i]) == null) ;
     }
     next = n;
     index = i;
   }
   public boolean hasNext() {
     return next != null;
   }
   Entry nextEntry() {
     if (modCount != expectedModCount)
       throw new ConcurrentModificationException();
     Entry e = next;
    if (e == null)
       throw new NoSuchElementException();
     Entry n = e.next;
     Entry[] t = table;
     int i = index;
     while (n == null && i > 0)
        n = t[--i]; index = i;
     next = n;
     return current = e;
   }
   public void remove() {
     if (current == null)
       throw new IllegalStateException();
     if (modCount != expectedModCount)
       throw new ConcurrentModificationException();
     Object k = current.key;
     current = null;
     HashMap.this.removeEntryForKey(k);
     expectedModCount = modCount;
   }
}

private class ValueIterator extends HashIterator {
   public Object next() {
     return nextEntry().value;
   }
}

private class KeyIterator extends HashIterator {
   public Object next() {
     return nextEntry().getKey();
   }
}

private class EntryIterator extends HashIterator {
   public Object next() {
     return nextEntry();
   }
}

Iterator newKeyIterator() {
   return new KeyIterator();
}

Iterator newValueIterator() {
   return new ValueIterator();
}

Iterator newEntryIterator() {  
   return new EntryIterator();
}

private transient Set entrySet = null;

public Set keySet() {
   Set ks = keySet;
   return (ks != null ? ks : (keySet = new KeySet()));
}

private class KeySet extends AbstractSet {
   public Iterator iterator() {
     return newKeyIterator();
   }
   public int size() {
     return size;
   }
   public boolean contains(Object o) {
     return containsKey(o);
   }
   public boolean remove(Object o) {
     return HashMap.this.removeEntryForKey(o) != null;
   }
   public void clear() {
     HashMap.this.clear();
   }
}

public Collection values() {
   Collection vs = values; return (vs != null ? vs : (values = new Values()));
}

private class Values extends AbstractCollection {
   public Iterator iterator() {
     return newValueIterator();
   }
   public int size() {
     return size;
   }
   public boolean contains(Object o) {
     return containsValue(o);
   }
   public void clear() {
     HashMap.this.clear();
   }
}

public Set entrySet() {
   Set es = entrySet;
   return (es != null ? es : (entrySet = new EntrySet()));
}

private class EntrySet extends AbstractSet {
   public Iterator iterator() {
     return newEntryIterator();
   }
   public boolean contains(Object o) {
     if (!(o instanceof Map.Entry))
       return false;
     Map.Entry e = (Map.Entry)o;
     Entry candidate = getEntry(e.getKey());
     return candidate != null && candidate.equals(e);
   }
   public boolean remove(Object o) {
     return removeMapping(o) != null;
   }
   public int size() {
     return size;
   }
   public void clear() {
     HashMap.this.clear();
   }
}

private void writeObject(java.io.ObjectOutputStream s) throws IOException {
   s.defaultWriteObject();
   s.writeInt(table.length);
   s.writeInt(size);
   for (Iterator i = entrySet().iterator(); i.hasNext(); ) {
     Map.Entry e = (Map.Entry) i.next();
     s.writeObject(e.getKey());
     s.writeObject(e.getValue());
   }
}

private static final long serialVersionUID = 362498820763181265L;

private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
   s.defaultReadObject();
   int numBuckets = s.readInt();
   table = new Entry[numBuckets];
   init();
   size = s.readInt(); for (int i=0;
   for (int i=0; i<size; i++) {
     Object key = s.readObject();
     Object value = s.readObject(); 
     putForCreate(key, value);
   }
}

int capacity() {
  return table.length;
}
float loadFactor() {
   return loadFactor;
}

HashMap是Java新Collection Framework中用来代替HashTable的一个实现,HashMap和HashTable的区别是: HashMap是未经同步的,而且允许null值。HashTable继承Dictionary,而且使用了Enumeration,所以被建议不要使用。
HashMap的声明如下:
public class HashMap extends AbstractMap implements Map, Cloneable,Serializable
有关AbstractMap:http://blog.youkuaiyun.com/treeroot/archive/2004/09/20/110343.aspx
有关Map:http://blog.youkuaiyun.com/treeroot/archive/2004/09/20/110331.aspx
有关Cloneable:http://blog.youkuaiyun.com/treeroot/archive/2004/09/07/96936.aspx
这个类比较复杂,这里只是重点分析了几个方法,特别是后面涉及到很多内部类都没有解释
不过都比较简单。

static final int DEFAULT_INITIAL_CAPACITY = 16; 默认初始化大小

static final int MAXIMUM_CAPACITY = 1 << 30; 最大初始化大小

static final float DEFAULT_LOAD_FACTOR = 0.75f; 默认加载因子

transient Entry[] table; 一个Entry类型的数组,数组的长度为2的指数。

transient int size; 映射的个数

int threshold; 下一次扩容时的值

final float loadFactor; 加载因子

transient volatile int modCount; 修改次数

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);
   int capacity = 1;
   while (capacity < initialCapacity)
     capacity <<= 1;
   this.loadFactor = loadFactor;
   threshold = (int)(capacity * loadFactor);
   table = new Entry[capacity];
   init();
}

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

public HashMap() {
   this.loadFactor = DEFAULT_LOAD_FACTOR;
   threshold = (int)(DEFAULT_INITIAL_CAPACITY);
      注意:这里应该是一个失误! 应该是:threshold =(int)(DEFAULT_INITIAL_CAPACITY * loadFactor);
   table = new Entry[DEFAULT_INITIAL_CAPACITY];
   init();
}

public HashMap(Map m) {
   this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY),                                   DEFAULT_LOAD_FACTOR);
   putAllForCreate(m);
}

void init() {}

static final Object NULL_KEY = new Object();

static Object maskNull(Object key){
   return (key == null ? NULL_KEY : key);
}

static Object unmaskNull(Object key) {
   return (key == NULL_KEY ? null : key);
}

static int hash(Object x) {
   int h = x.hashCode();
   h += ~(h << 9);
   h ^= (h >>> 14);
   h += (h << 4);
   h ^= (h >>> 10);
return h;
}
在HashTable中没有这个方法,也就是说HashTable中是直接用对象的hashCode值,但是HashMap做了改进 用这个算法来获得哈希值。

static boolean eq(Object x, Object y) {
   return x == y || x.equals(y);
}

static int indexFor(int h, int length) {
   return h & (length-1);
}
根据哈希值和数组的长度来返回该hash值在数组中的位置,只是简单的与关系。

public int size() {
   return size;
}

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

public Object get(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry e = table[i];
   while (true) {
     if (e == null) return e;
     if (e.hash == hash && eq(k, e.key)) return e.value;
     e = e.next;
   }
}
这个方法是获取数据的方法,首先获得哈希值,这里把null值掩饰了,并且hash值经过函数hash()修正。 然后计算该哈希值在数组中的索引值。如果该索引处的引用为null,表示HashMap中不存在这个映射。 否则的话遍历整个链表,这里找到了就返回,如果没有找到就遍历到链表末尾,返回null。这里的比较是这样的:e.hash==hash && eq(k,e.key) 也就是说如果hash不同就肯定认为不相等,eq就被短路了,只有在 hash相同的情况下才调用equals方法。现在我们该明白Object中说的如果两个对象equals返回true,他们的 hashCode应该相同的道理了吧。假如两个对象调用equals返回true,但是hashCode不一样,那么在HashMap 里就认为他们不相等。

public boolean containsKey(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry e = table[i];
   while (e != null) {
     if (e.hash == hash && eq(k, e.key)) return true;
     e = e.next;
   }
  return false;
}
这个方法比上面的简单,先找到哈希位置,再遍历整个链表,如果找到就返回true。

Entry getEntry(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry e = table[i];
   while (e != null && !(e.hash == hash && eq(k, e.key)))
     e = e.next;
   return e;
}
这个方法根据key值返回Entry节点,也是先获得索引位置,再遍历链表,如果没有找到返回的是null。

public Object put(Object key, Object value) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   for (Entry e = table[i]; e != null; e = e.next) {
     if (e.hash == hash && eq(k, e.key)) {
       Object oldValue = e.value;
       e.value = value;
       e.recordAccess(this);
       return oldValue;
     }
   }
   modCount++;
   addEntry(hash, k, value, i);
   return null;
}
首先获得hash索引位置,如果该位置的引用为null,那么直接插入一个映射,返回null。如果此处的引用不是null,必须遍历链表,如果找到一个相同的key,那么就更新该value,同时返回原来的value值。如果遍历完了没有找到,说明该key值不存在,还是插入一个映射。如果hash值足够离散的话,也就是说该索引没有被使用的话,那么不不用遍历链表了。相反,如果hash值不离散,极端的说如果是常数的话,所有的映射都会在这一个链表上,效率会极其低下。这里举一个最简单的例子,写两
个不同的类作为key插入到HashMap中,效率会远远不同。
class Good{
  int i;
  public Good(int i){
   this.i=i;
  }
  public boolean equals(Object o){
   return (o instanceof Good) && (this.i==((Good)o).i)
  }
  public int hashCode(){
   return i;
  }
}
class Bad{
  int i;
  public Good(int i){
    this.i=i;
  }
  public boolean equals(Object o){
    return (o instanceof Good) && (this.i==((Good)o).i)
  }
  public int hashCode(){
   return 0;
  }
}
执行代码:
Map m1=new HashMap();
Map m2=new HashMap();
for(int i=0;i<100;i++){
  m1.put(new Good(i),new Integer(i)); //这里效率非常高
}
for(int i=0;i<100;i++){
  m2.put(new Bad(i),new Integer(i)); //这里几乎要崩溃
}
上面的是两个非常极端的例子,执行一下就知道差别有多大。

private void putForCreate(Object key, Object value) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   for (Entry e = table[i]; e != null; e = e.next) {
     if (e.hash == hash && eq(k, e.key)) {
        e.value = value;
        return;
     }
   }
   createEntry(hash, k, value, i);
}

void putAllForCreate(Map m) {
   for (Iterator i = m.entrySet().iterator(); i.hasNext(); ) {
     Map.Entry e = (Map.Entry) i.next();
     putForCreate(e.getKey(), e.getValue());
   }
}
上面的两个方法是被构造函数和clone方法调用的。

void resize(int newCapacity) {
   Entry[] oldTable = table;
   int oldCapacity = oldTable.length;
   if (size < threshold || oldCapacity > newCapacity)
     return;
   Entry[] newTable = new Entry[newCapacity];
   transfer(newTable);
   table = newTable;
   threshold = (int)(newCapacity * loadFactor);
}
这个方法在需要的时候重新分配空间,相当于ArrayList的ensureCapacity方法,不过这个更加复杂。

void transfer(Entry[] newTable) {
   Entry[] src = table;
   int newCapacity = newTable.length;
   for (int j = 0; j < src.length; j++) {
     Entry e = src[j];
     if (e != null) {
       src[j] = null;
       do {
          Entry next = e.next;
          int i = indexFor(e.hash, newCapacity);
          e.next = newTable[i];
          newTable[i] = e;
          e = next;
       } while (e != null);
     }
   }
}
遍历原来的数组,如果该Entry不是null的话,说明有映射,然后遍历这个链表,把所有的映射插入到新的数组中,注意这里要从新计算索引位置。

public void putAll(Map t) {
   int n = t.size();
   if (n == 0)
     return;
   if (n >= threshold) {
     n = (int)(n / loadFactor + 1);
     if (n > MAXIMUM_CAPACITY)
       n = MAXIMUM_CAPACITY;
     int capacity = table.length;
     while (capacity < n) capacity <<= 1;
       resize(capacity);
   }
   for (Iterator i = t.entrySet().iterator(); i.hasNext(); ) {
     Map.Entry e = (Map.Entry) i.next();
     put(e.getKey(), e.getValue());
   }
}
这个方法先确定是否需要扩大空间,然后循环调用put方法。

public Object remove(Object key) {
   Entry e = removeEntryForKey(key);
   return (e == null ? e : e.value);
}

Entry removeEntryForKey(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry prev = table[i];
   Entry e = prev;
   while (e != null) {  如果e==null表示不存在
     Entry next = e.next;
     if (e.hash == hash && eq(k, e.key)) {
       modCount++;
       size--;
       if (prev == e)
         table[i] = next; 链表的第一个元素就是要删除的,这里最好加一句 e.next=null.
       else
         prev.next = next; 存在担不是链表的第一个元素, 这里最好加一句 e.next=null.
       e.recordRemoval(this);
       return e;
     }
     prev = e;
     e = next;
   }
  return e;   这里其实就是return null;
}
这个方法其实也不复杂,也是遍历链表,这里建议加一句e.next=null,可以改为
if(prev==e)
  table[i]=next;
else
  prev.next=next;
e.next=null; 这一句是多加的,可以提高效率。
这里简单说明我的看法:
因为e是被删除的节点,删除它其实就是指向它的指针指向它的后面一个节点。所以e可以作为GC回收的对象。
可以e还有一个next指针指向我们的数据,如果e没有被回收。而且此时e.next指向的节点也变为没用的了,但是
却有一个它的引用(e.next),所以虽然e的下一个节点没用了,但是却不能作为GC回收的对象,除非e先被回收。
虽然不一定会引起很大的问题,但是至少会影响GC的回收效率。就像数据库中的外键引用一样,删除起来很麻烦呀。

Entry removeMapping(Object o) {
   if (!(o instanceof Map.Entry))
     return null;
   Map.Entry entry = (Map.Entry)o;
   Object k = maskNull(entry.getKey());
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry prev = table[i];
   Entry e = prev;
   while (e != null) {
     Entry next = e.next;
     if (e.hash == hash && e.equals(entry)) {
       modCount++;
       size--;
       if (prev == e)
         table[i] = next;
       else
         prev.next = next;
       e.recordRemoval(this);
       return e;
      }
      prev = e;
      e = next;
   }
   return e;
}
这个方法和上面的一样。

public void clear() {
   modCount++;
   Entry tab[] = table;
   for (int i = 0; i < tab.length; i++)
     tab[i] = null;
   size = 0;
}
同样可以改进

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;
}

private boolean containsNullValue() {
   Entry tab[] = table;
   for (int i = 0; i < tab.length ; i++)
     for (Entry e = tab[i] ; e != null ; e = e.next)
       if (e.value == null) return true;
   return false;
}

public Object clone() {
   HashMap result = null;
   try {
     result = (HashMap)super.clone();
   }
   catch (CloneNotSupportedException e) { // assert false; }
   result.table = new Entry[table.length];
   result.entrySet = null;
   result.modCount = 0;
   result.size = 0;
   result.init();
   result.putAllForCreate(this);
   return result;
}

static class Entry implements Map.Entry {
   final Object key;
   Object value;
   final int hash;
   Entry next;
   Entry(int h, Object k, Object v, Entry n) {
     value = v;
     next = n;
     key = k;
     hash = h;
   }
   public Object getKey() {
     return unmaskNull(key);
   }
   public Object getValue() {
     return value;
   }
   public Object setValue(Object newValue) {
      Object oldValue = value;
      value = newValue;
      return oldValue;
   }
   public boolean equals(Object o) {
     if (!(o instanceof Map.Entry)) return false;
     Map.Entry e = (Map.Entry)o;
     Object k1 = getKey();
     Object k2 = e.getKey();
     if (k1 == k2 || (k1 != null && k1.equals(k2))) {
       Object v1 = getValue();
       Object v2 = e.getValue();
       if (v1 == v2 || (v1 != null && v1.equals(v2))) return true;
     }
     return false;
    }
    public int hashCode() {
      return (key==NULL_KEY ? 0 : key.hashCode()) ^ (value==null ? 0 : value.hashCode());
    }
    public String toString() {
      return getKey() + "=" + getValue();
    }
    void recordAccess(HashMap m) { }
    void recordRemoval(HashMap m) { }
}
一个静态内部类

void addEntry(int hash, Object key, Object value, int bucketIndex) {
    table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);
    if (size++ >= threshold)
      resize(2 * table.length);
}
注意这个方法,插入连表的头。
可以写成这样更好理解:
Entry oldHead=table[bucketIndex];
Entry newHead = new Entry(hash,key,value,oldHead);
table[bucketIndex]=newHead;

void createEntry(int hash, Object key, Object value, int bucketIndex) {
   table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);
   size++;
}

private abstract class HashIterator implements Iterator {
   Entry next;
   int expectedModCount;
   int index;
   Entry current;
   HashIterator() {
     expectedModCount = modCount;
     Entry[] t = table;
     int i = t.length;
     Entry n = null;
     if (size != 0) {
       while (i > 0 && (n = t[--i]) == null) ;
     }
     next = n;
     index = i;
   }
   public boolean hasNext() {
     return next != null;
   }
   Entry nextEntry() {
     if (modCount != expectedModCount)
       throw new ConcurrentModificationException();
     Entry e = next;
    if (e == null)
       throw new NoSuchElementException();
     Entry n = e.next;
     Entry[] t = table;
     int i = index;
     while (n == null && i > 0)
        n = t[--i]; index = i;
     next = n;
     return current = e;
   }
   public void remove() {
     if (current == null)
       throw new IllegalStateException();
     if (modCount != expectedModCount)
       throw new ConcurrentModificationException();
     Object k = current.key;
     current = null;
     HashMap.this.removeEntryForKey(k);
     expectedModCount = modCount;
   }
}

private class ValueIterator extends HashIterator {
   public Object next() {
     return nextEntry().value;
   }
}

private class KeyIterator extends HashIterator {
   public Object next() {
     return nextEntry().getKey();
   }
}

private class EntryIterator extends HashIterator {
   public Object next() {
     return nextEntry();
   }
}

Iterator newKeyIterator() {
   return new KeyIterator();
}

Iterator newValueIterator() {
   return new ValueIterator();
}

Iterator newEntryIterator() {  
   return new EntryIterator();
}

private transient Set entrySet = null;

public Set keySet() {
   Set ks = keySet;
   return (ks != null ? ks : (keySet = new KeySet()));
}

private class KeySet extends AbstractSet {
   public Iterator iterator() {
     return newKeyIterator();
   }
   public int size() {
     return size;
   }
   public boolean contains(Object o) {
     return containsKey(o);
   }
   public boolean remove(Object o) {
     return HashMap.this.removeEntryForKey(o) != null;
   }
   public void clear() {
     HashMap.this.clear();
   }
}

public Collection values() {
   Collection vs = values; return (vs != null ? vs : (values = new Values()));
}

private class Values extends AbstractCollection {
   public Iterator iterator() {
     return newValueIterator();
   }
   public int size() {
     return size;
   }
   public boolean contains(Object o) {
     return containsValue(o);
   }
   public void clear() {
     HashMap.this.clear();
   }
}

public Set entrySet() {
   Set es = entrySet;
   return (es != null ? es : (entrySet = new EntrySet()));
}

private class EntrySet extends AbstractSet {
   public Iterator iterator() {
     return newEntryIterator();
   }
   public boolean contains(Object o) {
     if (!(o instanceof Map.Entry))
       return false;
     Map.Entry e = (Map.Entry)o;
     Entry candidate = getEntry(e.getKey());
     return candidate != null && candidate.equals(e);
   }
   public boolean remove(Object o) {
     return removeMapping(o) != null;
   }
   public int size() {
     return size;
   }
   public void clear() {
     HashMap.this.clear();
   }
}

private void writeObject(java.io.ObjectOutputStream s) throws IOException {
   s.defaultWriteObject();
   s.writeInt(table.length);
   s.writeInt(size);
   for (Iterator i = entrySet().iterator(); i.hasNext(); ) {
     Map.Entry e = (Map.Entry) i.next();
     s.writeObject(e.getKey());
     s.writeObject(e.getValue());
   }
}

private static final long serialVersionUID = 362498820763181265L;

private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
   s.defaultReadObject();
   int numBuckets = s.readInt();
   table = new Entry[numBuckets];
   init();
   size = s.readInt(); for (int i=0;
   for (int i=0; i<size; i++) {
     Object key = s.readObject();
     Object value = s.readObject(); 
     putForCreate(key, value);
   }
}

int capacity() {
  return table.length;
}
float loadFactor() {
   return loadFactor;
}

  HashMap可谓JDK的一大实用工具,把各个Object映射起来,实现了“键--值”对应的快速存取。但实际里面做了些什么呢?

  在这之前,先介绍一下负载因子和容量的属性。大家都知道其实一个 HashMap 的实际容量就 因子*容量,其默认值是 16×0.75=12; 这个很重要,对效率很一定影响!当存入HashMap的对象超过这个容量时,HashMap 就会重新构造存取表。这就是一个大问题,我后面慢慢介绍,反正,如果你已经知道你大概要存放多少个对象,最好设为该实际容量的能接受的数字。

  两个关键的方法,put和get:

  先有这样一个概念,HashMap是声明了 Map,Cloneable, Serializable 接口,和继承了 AbstractMap 类,里面的 Iterator 其实主要都是其内部类HashIterator 和其他几个 iterator 类实现,当然还有一个很重要的继承了Map.Entry 的 Entry 内部类,由于大家都有源代码,大家有兴趣可以看看这部分,我主要想说明的是 Entry 内部类。它包含了hash,value,key 和next 这四个属性,很重要。put的源码如下

public Object put(Object key, Object value) {
Object k = maskNull(key);

  这个就是判断键值是否为空,并不很深奥,其实如果为空,它会返回一个static Object 作为键值,这就是为什么HashMap允许空键值的原因。

int hash = hash(k);
int i = indexFor(hash, table.length);

  这连续的两步就是 HashMap 最牛的地方!研究完我都汗颜了,其中 hash 就是通过 key 这个Object的 hashcode 进行 hash,然后通过 indexFor 获得在Object table的索引值。

  table???不要惊讶,其实HashMap也神不到哪里去,它就是用 table 来放的。最牛的就是用 hash 能正确的返回索引。其中的hash算法,我跟JDK的作者 Doug 联系过,他建议我看看《The art of programing vol3》可恨的是,我之前就一直在找,我都找不到,他这样一提,我就更加急了,可惜口袋空空啊!!!

  不知道大家有没有留意 put 其实是一个有返回的方法,它会把相同键值的 put 覆盖掉并返回旧的值!如下方法彻底说明了 HashMap 的结构,其实就是一个表加上在相应位置的Entry的链表:

for (Entry e = table[i]; e != null; e = e.next) {
 if (e.hash == hash && eq(k, e.key)) {
  Object oldvalue = e.value;
  e.value = value; //把新的值赋予给对应键值。
  e.recordAccess(this); //空方法,留待实现
  return oldvalue; //返回相同键值的对应的旧的值。
 }
}
modCount++; //结构性更改的次数
addEntry(hash, k, value, i); //添加新元素,关键所在!
return null; //没有相同的键值返回
}

  我们把关键的方法拿出来分析:

void addEntry(int hash, Object key, Object value, int bucketIndex) {
table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);

  因为 hash 的算法有可能令不同的键值有相同的hash码并有相同的table索引,如:key=“33”和key=Object g的hash都是-8901334,那它经过indexfor之后的索引一定都为i,这样在new的时候这个Entry的next就会指向这个原本的table[i],再有下一个也如此,形成一个链表,和put的循环对定e.next获得旧的值。到这里,HashMap的结构,大家也十分明白了吧?

if (size++ >= threshold) //这个threshold就是能实际容纳的量
resize(2 * table.length); //超出这个容量就会将Object table重构

  所谓的重构也不神,就是建一个两倍大的table(我在别的论坛上看到有人说是两倍加1,把我骗了),然后再一个个indexfor进去!注意!!这就是效率!!如果你能让你的HashMap不需要重构那么多次,效率会大大提高!

  说到这里也差不多了,get比put简单得多,大家,了解put,get也差不了多少了。对于collections我是认为,它是适合广泛的,当不完全适合特有的,如果大家的程序需要特殊的用途,自己写吧,其实很简单。(作者是这样跟我说的,他还建议我用LinkedHashMap,我看了源码以后发现,LinkHashMap其实就是继承HashMap的,然后override相应的方法,有兴趣的同人,自己looklook)建个 Object table,写相应的算法,就ok啦。

  举个例子吧,像 Vector,list 啊什么的其实都很简单,最多就多了的同步的声明,其实如果要实现像Vector那种,插入,删除不多的,可以用一个Object table来实现,按索引存取,添加等。

  如果插入,删除比较多的,可以建两个Object table,然后每个元素用含有next结构的,一个table存,如果要插入到i,但是i已经有元素,用next连起来,然后size++,并在另一个table记录其位置。
源码分析:HashMap
     

HashMap是Java新Collection Framework中用来代替HashTable的一个实现,HashMap和HashTable的区别是: HashMap是未经同步的,而且允许null值。HashTable继承Dictionary,而且使用了Enumeration,所以被建议不要使用。
HashMap的声明如下:
public class HashMap extends AbstractMap implements Map, Cloneable,Serializable
有关AbstractMap:http://blog.youkuaiyun.com/treeroot/archive/2004/09/20/110343.aspx
有关Map:http://blog.youkuaiyun.com/treeroot/archive/2004/09/20/110331.aspx
有关Cloneable:http://blog.youkuaiyun.com/treeroot/archive/2004/09/07/96936.aspx
这个类比较复杂,这里只是重点分析了几个方法,特别是后面涉及到很多内部类都没有解释
不过都比较简单。

static final int DEFAULT_INITIAL_CAPACITY = 16; 默认初始化大小

static final int MAXIMUM_CAPACITY = 1 << 30; 最大初始化大小

static final float DEFAULT_LOAD_FACTOR = 0.75f; 默认加载因子

transient Entry[] table; 一个Entry类型的数组,数组的长度为2的指数。

transient int size; 映射的个数

int threshold; 下一次扩容时的值

final float loadFactor; 加载因子

transient volatile int modCount; 修改次数

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);
   int capacity = 1;
   while (capacity < initialCapacity)
     capacity <<= 1;
   this.loadFactor = loadFactor;
   threshold = (int)(capacity * loadFactor);
   table = new Entry[capacity];
   init();
}

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

public HashMap() {
   this.loadFactor = DEFAULT_LOAD_FACTOR;
   threshold = (int)(DEFAULT_INITIAL_CAPACITY);
      注意:这里应该是一个失误! 应该是:threshold =(int)(DEFAULT_INITIAL_CAPACITY * loadFactor);
   table = new Entry[DEFAULT_INITIAL_CAPACITY];
   init();
}

public HashMap(Map m) {
   this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY),                                   DEFAULT_LOAD_FACTOR);
   putAllForCreate(m);
}

void init() {}

static final Object NULL_KEY = new Object();

static Object maskNull(Object key){
   return (key == null ? NULL_KEY : key);
}

static Object unmaskNull(Object key) {
   return (key == NULL_KEY ? null : key);
}

static int hash(Object x) {
   int h = x.hashCode();
   h += ~(h << 9);
   h ^= (h >>> 14);
   h += (h << 4);
   h ^= (h >>> 10);
return h;
}
在HashTable中没有这个方法,也就是说HashTable中是直接用对象的hashCode值,但是HashMap做了改进 用这个算法来获得哈希值。

static boolean eq(Object x, Object y) {
   return x == y || x.equals(y);
}

static int indexFor(int h, int length) {
   return h & (length-1);
}
根据哈希值和数组的长度来返回该hash值在数组中的位置,只是简单的与关系。

public int size() {
   return size;
}

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

public Object get(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry e = table[i];
   while (true) {
     if (e == null) return e;
     if (e.hash == hash && eq(k, e.key)) return e.value;
     e = e.next;
   }
}
这个方法是获取数据的方法,首先获得哈希值,这里把null值掩饰了,并且hash值经过函数hash()修正。 然后计算该哈希值在数组中的索引值。如果该索引处的引用为null,表示HashMap中不存在这个映射。 否则的话遍历整个链表,这里找到了就返回,如果没有找到就遍历到链表末尾,返回null。这里的比较是这样的:e.hash==hash && eq(k,e.key) 也就是说如果hash不同就肯定认为不相等,eq就被短路了,只有在 hash相同的情况下才调用equals方法。现在我们该明白Object中说的如果两个对象equals返回true,他们的 hashCode应该相同的道理了吧。假如两个对象调用equals返回true,但是hashCode不一样,那么在HashMap 里就认为他们不相等。

public boolean containsKey(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry e = table[i];
   while (e != null) {
     if (e.hash == hash && eq(k, e.key)) return true;
     e = e.next;
   }
  return false;
}
这个方法比上面的简单,先找到哈希位置,再遍历整个链表,如果找到就返回true。

Entry getEntry(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry e = table[i];
   while (e != null && !(e.hash == hash && eq(k, e.key)))
     e = e.next;
   return e;
}
这个方法根据key值返回Entry节点,也是先获得索引位置,再遍历链表,如果没有找到返回的是null。

public Object put(Object key, Object value) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   for (Entry e = table[i]; e != null; e = e.next) {
     if (e.hash == hash && eq(k, e.key)) {
       Object oldValue = e.value;
       e.value = value;
       e.recordAccess(this);
       return oldValue;
     }
   }
   modCount++;
   addEntry(hash, k, value, i);
   return null;
}
首先获得hash索引位置,如果该位置的引用为null,那么直接插入一个映射,返回null。如果此处的引用不是null,必须遍历链表,如果找到一个相同的key,那么就更新该value,同时返回原来的value值。如果遍历完了没有找到,说明该key值不存在,还是插入一个映射。如果hash值足够离散的话,也就是说该索引没有被使用的话,那么不不用遍历链表了。相反,如果hash值不离散,极端的说如果是常数的话,所有的映射都会在这一个链表上,效率会极其低下。这里举一个最简单的例子,写两
个不同的类作为key插入到HashMap中,效率会远远不同。
class Good{
  int i;
  public Good(int i){
   this.i=i;
  }
  public boolean equals(Object o){
   return (o instanceof Good) && (this.i==((Good)o).i)
  }
  public int hashCode(){
   return i;
  }
}
class Bad{
  int i;
  public Good(int i){
    this.i=i;
  }
  public boolean equals(Object o){
    return (o instanceof Good) && (this.i==((Good)o).i)
  }
  public int hashCode(){
   return 0;
  }
}
执行代码:
Map m1=new HashMap();
Map m2=new HashMap();
for(int i=0;i<100;i++){
  m1.put(new Good(i),new Integer(i)); //这里效率非常高
}
for(int i=0;i<100;i++){
  m2.put(new Bad(i),new Integer(i)); //这里几乎要崩溃
}
上面的是两个非常极端的例子,执行一下就知道差别有多大。

private void putForCreate(Object key, Object value) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   for (Entry e = table[i]; e != null; e = e.next) {
     if (e.hash == hash && eq(k, e.key)) {
        e.value = value;
        return;
     }
   }
   createEntry(hash, k, value, i);
}

void putAllForCreate(Map m) {
   for (Iterator i = m.entrySet().iterator(); i.hasNext(); ) {
     Map.Entry e = (Map.Entry) i.next();
     putForCreate(e.getKey(), e.getValue());
   }
}
上面的两个方法是被构造函数和clone方法调用的。

void resize(int newCapacity) {
   Entry[] oldTable = table;
   int oldCapacity = oldTable.length;
   if (size < threshold || oldCapacity > newCapacity)
     return;
   Entry[] newTable = new Entry[newCapacity];
   transfer(newTable);
   table = newTable;
   threshold = (int)(newCapacity * loadFactor);
}
这个方法在需要的时候重新分配空间,相当于ArrayList的ensureCapacity方法,不过这个更加复杂。

void transfer(Entry[] newTable) {
   Entry[] src = table;
   int newCapacity = newTable.length;
   for (int j = 0; j < src.length; j++) {
     Entry e = src[j];
     if (e != null) {
       src[j] = null;
       do {
          Entry next = e.next;
          int i = indexFor(e.hash, newCapacity);
          e.next = newTable[i];
          newTable[i] = e;
          e = next;
       } while (e != null);
     }
   }
}
遍历原来的数组,如果该Entry不是null的话,说明有映射,然后遍历这个链表,把所有的映射插入到新的数组中,注意这里要从新计算索引位置。

public void putAll(Map t) {
   int n = t.size();
   if (n == 0)
     return;
   if (n >= threshold) {
     n = (int)(n / loadFactor + 1);
     if (n > MAXIMUM_CAPACITY)
       n = MAXIMUM_CAPACITY;
     int capacity = table.length;
     while (capacity < n) capacity <<= 1;
       resize(capacity);
   }
   for (Iterator i = t.entrySet().iterator(); i.hasNext(); ) {
     Map.Entry e = (Map.Entry) i.next();
     put(e.getKey(), e.getValue());
   }
}
这个方法先确定是否需要扩大空间,然后循环调用put方法。

public Object remove(Object key) {
   Entry e = removeEntryForKey(key);
   return (e == null ? e : e.value);
}

Entry removeEntryForKey(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry prev = table[i];
   Entry e = prev;
   while (e != null) {  如果e==null表示不存在
     Entry next = e.next;
     if (e.hash == hash && eq(k, e.key)) {
       modCount++;
       size--;
       if (prev == e)
         table[i] = next; 链表的第一个元素就是要删除的,这里最好加一句 e.next=null.
       else
         prev.next = next; 存在担不是链表的第一个元素, 这里最好加一句 e.next=null.
       e.recordRemoval(this);
       return e;
     }
     prev = e;
     e = next;
   }
  return e;   这里其实就是return null;
}
这个方法其实也不复杂,也是遍历链表,这里建议加一句e.next=null,可以改为
if(prev==e)
  table[i]=next;
else
  prev.next=next;
e.next=null; 这一句是多加的,可以提高效率。
这里简单说明我的看法:
因为e是被删除的节点,删除它其实就是指向它的指针指向它的后面一个节点。所以e可以作为GC回收的对象。
可以e还有一个next指针指向我们的数据,如果e没有被回收。而且此时e.next指向的节点也变为没用的了,但是
却有一个它的引用(e.next),所以虽然e的下一个节点没用了,但是却不能作为GC回收的对象,除非e先被回收。
虽然不一定会引起很大的问题,但是至少会影响GC的回收效率。就像数据库中的外键引用一样,删除起来很麻烦呀。

Entry removeMapping(Object o) {
   if (!(o instanceof Map.Entry))
     return null;
   Map.Entry entry = (Map.Entry)o;
   Object k = maskNull(entry.getKey());
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry prev = table[i];
   Entry e = prev;
   while (e != null) {
     Entry next = e.next;
     if (e.hash == hash && e.equals(entry)) {
       modCount++;
       size--;
       if (prev == e)
         table[i] = next;
       else
         prev.next = next;
       e.recordRemoval(this);
       return e;
      }
      prev = e;
      e = next;
   }
   return e;
}
这个方法和上面的一样。

public void clear() {
   modCount++;
   Entry tab[] = table;
   for (int i = 0; i < tab.length; i++)
     tab[i] = null;
   size = 0;
}
同样可以改进

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;
}

private boolean containsNullValue() {
   Entry tab[] = table;
   for (int i = 0; i < tab.length ; i++)
     for (Entry e = tab[i] ; e != null ; e = e.next)
       if (e.value == null) return true;
   return false;
}

public Object clone() {
   HashMap result = null;
   try {
     result = (HashMap)super.clone();
   }
   catch (CloneNotSupportedException e) { // assert false; }
   result.table = new Entry[table.length];
   result.entrySet = null;
   result.modCount = 0;
   result.size = 0;
   result.init();
   result.putAllForCreate(this);
   return result;
}

static class Entry implements Map.Entry {
   final Object key;
   Object value;
   final int hash;
   Entry next;
   Entry(int h, Object k, Object v, Entry n) {
     value = v;
     next = n;
     key = k;
     hash = h;
   }
   public Object getKey() {
     return unmaskNull(key);
   }
   public Object getValue() {
     return value;
   }
   public Object setValue(Object newValue) {
      Object oldValue = value;
      value = newValue;
      return oldValue;
   }
   public boolean equals(Object o) {
     if (!(o instanceof Map.Entry)) return false;
     Map.Entry e = (Map.Entry)o;
     Object k1 = getKey();
     Object k2 = e.getKey();
     if (k1 == k2 || (k1 != null && k1.equals(k2))) {
       Object v1 = getValue();
       Object v2 = e.getValue();
       if (v1 == v2 || (v1 != null && v1.equals(v2))) return true;
     }
     return false;
    }
    public int hashCode() {
      return (key==NULL_KEY ? 0 : key.hashCode()) ^ (value==null ? 0 : value.hashCode());
    }
    public String toString() {
      return getKey() + "=" + getValue();
    }
    void recordAccess(HashMap m) { }
    void recordRemoval(HashMap m) { }
}
一个静态内部类

void addEntry(int hash, Object key, Object value, int bucketIndex) {
    table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);
    if (size++ >= threshold)
      resize(2 * table.length);
}
注意这个方法,插入连表的头。
可以写成这样更好理解:
Entry oldHead=table[bucketIndex];
Entry newHead = new Entry(hash,key,value,oldHead);
table[bucketIndex]=newHead;

void createEntry(int hash, Object key, Object value, int bucketIndex) {
   table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);
   size++;
}

private abstract class HashIterator implements Iterator {
   Entry next;
   int expectedModCount;
   int index;
   Entry current;
   HashIterator() {
     expectedModCount = modCount;
     Entry[] t = table;
     int i = t.length;
     Entry n = null;
     if (size != 0) {
       while (i > 0 && (n = t[--i]) == null) ;
     }
     next = n;
     index = i;
   }
   public boolean hasNext() {
     return next != null;
   }
   Entry nextEntry() {
     if (modCount != expectedModCount)
       throw new ConcurrentModificationException();
     Entry e = next;
    if (e == null)
       throw new NoSuchElementException();
     Entry n = e.next;
     Entry[] t = table;
     int i = index;
     while (n == null && i > 0)
        n = t[--i]; index = i;
     next = n;
     return current = e;
   }
   public void remove() {
     if (current == null)
       throw new IllegalStateException();
     if (modCount != expectedModCount)
       throw new ConcurrentModificationException();
     Object k = current.key;
     current = null;
     HashMap.this.removeEntryForKey(k);
     expectedModCount = modCount;
   }
}

private class ValueIterator extends HashIterator {
   public Object next() {
     return nextEntry().value;
   }
}

private class KeyIterator extends HashIterator {
   public Object next() {
     return nextEntry().getKey();
   }
}

private class EntryIterator extends HashIterator {
   public Object next() {
     return nextEntry();
   }
}

Iterator newKeyIterator() {
   return new KeyIterator();
}

Iterator newValueIterator() {
   return new ValueIterator();
}

Iterator newEntryIterator() {  
   return new EntryIterator();
}

private transient Set entrySet = null;

public Set keySet() {
   Set ks = keySet;
   return (ks != null ? ks : (keySet = new KeySet()));
}

private class KeySet extends AbstractSet {
   public Iterator iterator() {
     return newKeyIterator();
   }
   public int size() {
     return size;
   }
   public boolean contains(Object o) {
     return containsKey(o);
   }
   public boolean remove(Object o) {
     return HashMap.this.removeEntryForKey(o) != null;
   }
   public void clear() {
     HashMap.this.clear();
   }
}

public Collection values() {
   Collection vs = values; return (vs != null ? vs : (values = new Values()));
}

private class Values extends AbstractCollection {
   public Iterator iterator() {
     return newValueIterator();
   }
   public int size() {
     return size;
   }
   public boolean contains(Object o) {
     return containsValue(o);
   }
   public void clear() {
     HashMap.this.clear();
   }
}

public Set entrySet() {
   Set es = entrySet;
   return (es != null ? es : (entrySet = new EntrySet()));
}

private class EntrySet extends AbstractSet {
   public Iterator iterator() {
     return newEntryIterator();
   }
   public boolean contains(Object o) {
     if (!(o instanceof Map.Entry))
       return false;
     Map.Entry e = (Map.Entry)o;
     Entry candidate = getEntry(e.getKey());
     return candidate != null && candidate.equals(e);
   }
   public boolean remove(Object o) {
     return removeMapping(o) != null;
   }
   public int size() {
     return size;
   }
   public void clear() {
     HashMap.this.clear();
   }
}

private void writeObject(java.io.ObjectOutputStream s) throws IOException {
   s.defaultWriteObject();
   s.writeInt(table.length);
   s.writeInt(size);
   for (Iterator i = entrySet().iterator(); i.hasNext(); ) {
     Map.Entry e = (Map.Entry) i.next();
     s.writeObject(e.getKey());
     s.writeObject(e.getValue());
   }
}

private static final long serialVersionUID = 362498820763181265L;

private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
   s.defaultReadObject();
   int numBuckets = s.readInt();
   table = new Entry[numBuckets];
   init();
   size = s.readInt(); for (int i=0;
   for (int i=0; i<size; i++) {
     Object key = s.readObject();
     Object value = s.readObject(); 
     putForCreate(key, value);
   }
}

int capacity() {
  return table.length;
}
float loadFactor() {
   return loadFactor;
}

HashMap是Java新Collection Framework中用来代替HashTable的一个实现,HashMap和HashTable的区别是: HashMap是未经同步的,而且允许null值。HashTable继承Dictionary,而且使用了Enumeration,所以被建议不要使用。
HashMap的声明如下:
public class HashMap extends AbstractMap implements Map, Cloneable,Serializable
有关AbstractMap:http://blog.youkuaiyun.com/treeroot/archive/2004/09/20/110343.aspx
有关Map:http://blog.youkuaiyun.com/treeroot/archive/2004/09/20/110331.aspx
有关Cloneable:http://blog.youkuaiyun.com/treeroot/archive/2004/09/07/96936.aspx
这个类比较复杂,这里只是重点分析了几个方法,特别是后面涉及到很多内部类都没有解释
不过都比较简单。

static final int DEFAULT_INITIAL_CAPACITY = 16; 默认初始化大小

static final int MAXIMUM_CAPACITY = 1 << 30; 最大初始化大小

static final float DEFAULT_LOAD_FACTOR = 0.75f; 默认加载因子

transient Entry[] table; 一个Entry类型的数组,数组的长度为2的指数。

transient int size; 映射的个数

int threshold; 下一次扩容时的值

final float loadFactor; 加载因子

transient volatile int modCount; 修改次数

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);
   int capacity = 1;
   while (capacity < initialCapacity)
     capacity <<= 1;
   this.loadFactor = loadFactor;
   threshold = (int)(capacity * loadFactor);
   table = new Entry[capacity];
   init();
}

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

public HashMap() {
   this.loadFactor = DEFAULT_LOAD_FACTOR;
   threshold = (int)(DEFAULT_INITIAL_CAPACITY);
      注意:这里应该是一个失误! 应该是:threshold =(int)(DEFAULT_INITIAL_CAPACITY * loadFactor);
   table = new Entry[DEFAULT_INITIAL_CAPACITY];
   init();
}

public HashMap(Map m) {
   this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY),                                   DEFAULT_LOAD_FACTOR);
   putAllForCreate(m);
}

void init() {}

static final Object NULL_KEY = new Object();

static Object maskNull(Object key){
   return (key == null ? NULL_KEY : key);
}

static Object unmaskNull(Object key) {
   return (key == NULL_KEY ? null : key);
}

static int hash(Object x) {
   int h = x.hashCode();
   h += ~(h << 9);
   h ^= (h >>> 14);
   h += (h << 4);
   h ^= (h >>> 10);
return h;
}
在HashTable中没有这个方法,也就是说HashTable中是直接用对象的hashCode值,但是HashMap做了改进 用这个算法来获得哈希值。

static boolean eq(Object x, Object y) {
   return x == y || x.equals(y);
}

static int indexFor(int h, int length) {
   return h & (length-1);
}
根据哈希值和数组的长度来返回该hash值在数组中的位置,只是简单的与关系。

public int size() {
   return size;
}

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

public Object get(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry e = table[i];
   while (true) {
     if (e == null) return e;
     if (e.hash == hash && eq(k, e.key)) return e.value;
     e = e.next;
   }
}
这个方法是获取数据的方法,首先获得哈希值,这里把null值掩饰了,并且hash值经过函数hash()修正。 然后计算该哈希值在数组中的索引值。如果该索引处的引用为null,表示HashMap中不存在这个映射。 否则的话遍历整个链表,这里找到了就返回,如果没有找到就遍历到链表末尾,返回null。这里的比较是这样的:e.hash==hash && eq(k,e.key) 也就是说如果hash不同就肯定认为不相等,eq就被短路了,只有在 hash相同的情况下才调用equals方法。现在我们该明白Object中说的如果两个对象equals返回true,他们的 hashCode应该相同的道理了吧。假如两个对象调用equals返回true,但是hashCode不一样,那么在HashMap 里就认为他们不相等。

public boolean containsKey(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry e = table[i];
   while (e != null) {
     if (e.hash == hash && eq(k, e.key)) return true;
     e = e.next;
   }
  return false;
}
这个方法比上面的简单,先找到哈希位置,再遍历整个链表,如果找到就返回true。

Entry getEntry(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry e = table[i];
   while (e != null && !(e.hash == hash && eq(k, e.key)))
     e = e.next;
   return e;
}
这个方法根据key值返回Entry节点,也是先获得索引位置,再遍历链表,如果没有找到返回的是null。

public Object put(Object key, Object value) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   for (Entry e = table[i]; e != null; e = e.next) {
     if (e.hash == hash && eq(k, e.key)) {
       Object oldValue = e.value;
       e.value = value;
       e.recordAccess(this);
       return oldValue;
     }
   }
   modCount++;
   addEntry(hash, k, value, i);
   return null;
}
首先获得hash索引位置,如果该位置的引用为null,那么直接插入一个映射,返回null。如果此处的引用不是null,必须遍历链表,如果找到一个相同的key,那么就更新该value,同时返回原来的value值。如果遍历完了没有找到,说明该key值不存在,还是插入一个映射。如果hash值足够离散的话,也就是说该索引没有被使用的话,那么不不用遍历链表了。相反,如果hash值不离散,极端的说如果是常数的话,所有的映射都会在这一个链表上,效率会极其低下。这里举一个最简单的例子,写两
个不同的类作为key插入到HashMap中,效率会远远不同。
class Good{
  int i;
  public Good(int i){
   this.i=i;
  }
  public boolean equals(Object o){
   return (o instanceof Good) && (this.i==((Good)o).i)
  }
  public int hashCode(){
   return i;
  }
}
class Bad{
  int i;
  public Good(int i){
    this.i=i;
  }
  public boolean equals(Object o){
    return (o instanceof Good) && (this.i==((Good)o).i)
  }
  public int hashCode(){
   return 0;
  }
}
执行代码:
Map m1=new HashMap();
Map m2=new HashMap();
for(int i=0;i<100;i++){
  m1.put(new Good(i),new Integer(i)); //这里效率非常高
}
for(int i=0;i<100;i++){
  m2.put(new Bad(i),new Integer(i)); //这里几乎要崩溃
}
上面的是两个非常极端的例子,执行一下就知道差别有多大。

private void putForCreate(Object key, Object value) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   for (Entry e = table[i]; e != null; e = e.next) {
     if (e.hash == hash && eq(k, e.key)) {
        e.value = value;
        return;
     }
   }
   createEntry(hash, k, value, i);
}

void putAllForCreate(Map m) {
   for (Iterator i = m.entrySet().iterator(); i.hasNext(); ) {
     Map.Entry e = (Map.Entry) i.next();
     putForCreate(e.getKey(), e.getValue());
   }
}
上面的两个方法是被构造函数和clone方法调用的。

void resize(int newCapacity) {
   Entry[] oldTable = table;
   int oldCapacity = oldTable.length;
   if (size < threshold || oldCapacity > newCapacity)
     return;
   Entry[] newTable = new Entry[newCapacity];
   transfer(newTable);
   table = newTable;
   threshold = (int)(newCapacity * loadFactor);
}
这个方法在需要的时候重新分配空间,相当于ArrayList的ensureCapacity方法,不过这个更加复杂。

void transfer(Entry[] newTable) {
   Entry[] src = table;
   int newCapacity = newTable.length;
   for (int j = 0; j < src.length; j++) {
     Entry e = src[j];
     if (e != null) {
       src[j] = null;
       do {
          Entry next = e.next;
          int i = indexFor(e.hash, newCapacity);
          e.next = newTable[i];
          newTable[i] = e;
          e = next;
       } while (e != null);
     }
   }
}
遍历原来的数组,如果该Entry不是null的话,说明有映射,然后遍历这个链表,把所有的映射插入到新的数组中,注意这里要从新计算索引位置。

public void putAll(Map t) {
   int n = t.size();
   if (n == 0)
     return;
   if (n >= threshold) {
     n = (int)(n / loadFactor + 1);
     if (n > MAXIMUM_CAPACITY)
       n = MAXIMUM_CAPACITY;
     int capacity = table.length;
     while (capacity < n) capacity <<= 1;
       resize(capacity);
   }
   for (Iterator i = t.entrySet().iterator(); i.hasNext(); ) {
     Map.Entry e = (Map.Entry) i.next();
     put(e.getKey(), e.getValue());
   }
}
这个方法先确定是否需要扩大空间,然后循环调用put方法。

public Object remove(Object key) {
   Entry e = removeEntryForKey(key);
   return (e == null ? e : e.value);
}

Entry removeEntryForKey(Object key) {
   Object k = maskNull(key);
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry prev = table[i];
   Entry e = prev;
   while (e != null) {  如果e==null表示不存在
     Entry next = e.next;
     if (e.hash == hash && eq(k, e.key)) {
       modCount++;
       size--;
       if (prev == e)
         table[i] = next; 链表的第一个元素就是要删除的,这里最好加一句 e.next=null.
       else
         prev.next = next; 存在担不是链表的第一个元素, 这里最好加一句 e.next=null.
       e.recordRemoval(this);
       return e;
     }
     prev = e;
     e = next;
   }
  return e;   这里其实就是return null;
}
这个方法其实也不复杂,也是遍历链表,这里建议加一句e.next=null,可以改为
if(prev==e)
  table[i]=next;
else
  prev.next=next;
e.next=null; 这一句是多加的,可以提高效率。
这里简单说明我的看法:
因为e是被删除的节点,删除它其实就是指向它的指针指向它的后面一个节点。所以e可以作为GC回收的对象。
可以e还有一个next指针指向我们的数据,如果e没有被回收。而且此时e.next指向的节点也变为没用的了,但是
却有一个它的引用(e.next),所以虽然e的下一个节点没用了,但是却不能作为GC回收的对象,除非e先被回收。
虽然不一定会引起很大的问题,但是至少会影响GC的回收效率。就像数据库中的外键引用一样,删除起来很麻烦呀。

Entry removeMapping(Object o) {
   if (!(o instanceof Map.Entry))
     return null;
   Map.Entry entry = (Map.Entry)o;
   Object k = maskNull(entry.getKey());
   int hash = hash(k);
   int i = indexFor(hash, table.length);
   Entry prev = table[i];
   Entry e = prev;
   while (e != null) {
     Entry next = e.next;
     if (e.hash == hash && e.equals(entry)) {
       modCount++;
       size--;
       if (prev == e)
         table[i] = next;
       else
         prev.next = next;
       e.recordRemoval(this);
       return e;
      }
      prev = e;
      e = next;
   }
   return e;
}
这个方法和上面的一样。

public void clear() {
   modCount++;
   Entry tab[] = table;
   for (int i = 0; i < tab.length; i++)
     tab[i] = null;
   size = 0;
}
同样可以改进

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;
}

private boolean containsNullValue() {
   Entry tab[] = table;
   for (int i = 0; i < tab.length ; i++)
     for (Entry e = tab[i] ; e != null ; e = e.next)
       if (e.value == null) return true;
   return false;
}

public Object clone() {
   HashMap result = null;
   try {
     result = (HashMap)super.clone();
   }
   catch (CloneNotSupportedException e) { // assert false; }
   result.table = new Entry[table.length];
   result.entrySet = null;
   result.modCount = 0;
   result.size = 0;
   result.init();
   result.putAllForCreate(this);
   return result;
}

static class Entry implements Map.Entry {
   final Object key;
   Object value;
   final int hash;
   Entry next;
   Entry(int h, Object k, Object v, Entry n) {
     value = v;
     next = n;
     key = k;
     hash = h;
   }
   public Object getKey() {
     return unmaskNull(key);
   }
   public Object getValue() {
     return value;
   }
   public Object setValue(Object newValue) {
      Object oldValue = value;
      value = newValue;
      return oldValue;
   }
   public boolean equals(Object o) {
     if (!(o instanceof Map.Entry)) return false;
     Map.Entry e = (Map.Entry)o;
     Object k1 = getKey();
     Object k2 = e.getKey();
     if (k1 == k2 || (k1 != null && k1.equals(k2))) {
       Object v1 = getValue();
       Object v2 = e.getValue();
       if (v1 == v2 || (v1 != null && v1.equals(v2))) return true;
     }
     return false;
    }
    public int hashCode() {
      return (key==NULL_KEY ? 0 : key.hashCode()) ^ (value==null ? 0 : value.hashCode());
    }
    public String toString() {
      return getKey() + "=" + getValue();
    }
    void recordAccess(HashMap m) { }
    void recordRemoval(HashMap m) { }
}
一个静态内部类

void addEntry(int hash, Object key, Object value, int bucketIndex) {
    table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);
    if (size++ >= threshold)
      resize(2 * table.length);
}
注意这个方法,插入连表的头。
可以写成这样更好理解:
Entry oldHead=table[bucketIndex];
Entry newHead = new Entry(hash,key,value,oldHead);
table[bucketIndex]=newHead;

void createEntry(int hash, Object key, Object value, int bucketIndex) {
   table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);
   size++;
}

private abstract class HashIterator implements Iterator {
   Entry next;
   int expectedModCount;
   int index;
   Entry current;
   HashIterator() {
     expectedModCount = modCount;
     Entry[] t = table;
     int i = t.length;
     Entry n = null;
     if (size != 0) {
       while (i > 0 && (n = t[--i]) == null) ;
     }
     next = n;
     index = i;
   }
   public boolean hasNext() {
     return next != null;
   }
   Entry nextEntry() {
     if (modCount != expectedModCount)
       throw new ConcurrentModificationException();
     Entry e = next;
    if (e == null)
       throw new NoSuchElementException();
     Entry n = e.next;
     Entry[] t = table;
     int i = index;
     while (n == null && i > 0)
        n = t[--i]; index = i;
     next = n;
     return current = e;
   }
   public void remove() {
     if (current == null)
       throw new IllegalStateException();
     if (modCount != expectedModCount)
       throw new ConcurrentModificationException();
     Object k = current.key;
     current = null;
     HashMap.this.removeEntryForKey(k);
     expectedModCount = modCount;
   }
}

private class ValueIterator extends HashIterator {
   public Object next() {
     return nextEntry().value;
   }
}

private class KeyIterator extends HashIterator {
   public Object next() {
     return nextEntry().getKey();
   }
}

private class EntryIterator extends HashIterator {
   public Object next() {
     return nextEntry();
   }
}

Iterator newKeyIterator() {
   return new KeyIterator();
}

Iterator newValueIterator() {
   return new ValueIterator();
}

Iterator newEntryIterator() {  
   return new EntryIterator();
}

private transient Set entrySet = null;

public Set keySet() {
   Set ks = keySet;
   return (ks != null ? ks : (keySet = new KeySet()));
}

private class KeySet extends AbstractSet {
   public Iterator iterator() {
     return newKeyIterator();
   }
   public int size() {
     return size;
   }
   public boolean contains(Object o) {
     return containsKey(o);
   }
   public boolean remove(Object o) {
     return HashMap.this.removeEntryForKey(o) != null;
   }
   public void clear() {
     HashMap.this.clear();
   }
}

public Collection values() {
   Collection vs = values; return (vs != null ? vs : (values = new Values()));
}

private class Values extends AbstractCollection {
   public Iterator iterator() {
     return newValueIterator();
   }
   public int size() {
     return size;
   }
   public boolean contains(Object o) {
     return containsValue(o);
   }
   public void clear() {
     HashMap.this.clear();
   }
}

public Set entrySet() {
   Set es = entrySet;
   return (es != null ? es : (entrySet = new EntrySet()));
}

private class EntrySet extends AbstractSet {
   public Iterator iterator() {
     return newEntryIterator();
   }
   public boolean contains(Object o) {
     if (!(o instanceof Map.Entry))
       return false;
     Map.Entry e = (Map.Entry)o;
     Entry candidate = getEntry(e.getKey());
     return candidate != null && candidate.equals(e);
   }
   public boolean remove(Object o) {
     return removeMapping(o) != null;
   }
   public int size() {
     return size;
   }
   public void clear() {
     HashMap.this.clear();
   }
}

private void writeObject(java.io.ObjectOutputStream s) throws IOException {
   s.defaultWriteObject();
   s.writeInt(table.length);
   s.writeInt(size);
   for (Iterator i = entrySet().iterator(); i.hasNext(); ) {
     Map.Entry e = (Map.Entry) i.next();
     s.writeObject(e.getKey());
     s.writeObject(e.getValue());
   }
}

private static final long serialVersionUID = 362498820763181265L;

private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
   s.defaultReadObject();
   int numBuckets = s.readInt();
   table = new Entry[numBuckets];
   init();
   size = s.readInt(); for (int i=0;
   for (int i=0; i<size; i++) {
     Object key = s.readObject();
     Object value = s.readObject(); 
     putForCreate(key, value);
   }
}

int capacity() {
  return table.length;
}
float loadFactor() {
   return loadFactor;
}

基于深度学习的垃圾邮件识别 垃圾邮件的大量存在严重威胁着电子邮件系统的安全和效率。基于深度学习的垃圾邮件识别技术已经成为目前最有效的解决方案之一。本文将详细介绍使用深度学习技术进行垃圾邮件识别的方法,并提供相应的代码实现。 随着电子邮件的广泛应用,垃圾邮件的数量不断增加,传统的规则和统计方法已经无法满足对垃圾邮件进行准确识别的需求。深度学习技术以其强大的模式识别能力和自动特征提取能力成为解决垃圾邮件识别难题的有效工具。 阅读详情

相关推荐

保姆级教程:用Altium Designer 24从零画一块PCB(附完整库文件与避坑清单)

本文提供了一份详细的Altium Designer 24(AD24)PCB设计教程,从工程创建到最终文件输出,涵盖元件库管理、原理图设计、PCB布局布线等全流程。特别适合初学者,包含STM32最小系统板的实战案例,附完整库文件和避坑清单,帮助用户快速掌握AD24的核心功能与技巧。

weixin_27697385的博客 248

哈希表之哈希集和哈希映射详解(c++),包括hash.count()和hash.fine()的使用。

哈希表中的哈希集和哈希映射,包括建立哈希集,插入键,删除键,搜索键,遍历哈希集详解;和建立哈希映射,插入键值对,删除键值对,搜索键值对,遍历哈希映射详解。包括hash.count()和hash.fine()的使用

weixin_45249333的博客 4975

高德地图+Three.js实战:手把手教你打造数字农场3D可视化大屏(附完整源码)

本文详细介绍了如何利用高德地图JS API与Three.js技术栈,构建一个功能强大的数字农场3D可视化大屏。文章从技术选型、架构设计出发,深入讲解了坐标对齐、图层管理、性能优化等核心工程实践,并提供了完整的模块化代码示例,旨在为智慧农业及类似场景的3D地理可视化项目提供一套可复用的解决方案。

k8s6orchestrator的博客 890

C++中的Hashmap

C++中的Hashmap Hashmap 结构 HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长。 HashMap是非线程安全的,只是用于单线程环境下,但是Hash Table是线程安全的(https://www.cnblogs.com/williamjie/p/9099141.html) 哈希表(key,value) 是把key值通过一个固定的算法即哈希函数转换成一个整型数字,然后就将该数字对数组长度进行取余,取

Misty_Rain_的博客 3万+

抖音直播间人气接口算法 抖音协议

抖音直播间人气接口算法 抖音协议 抖音算法

Q44804487的博客 1万+

OLED 驱动模块程序代码

作为嵌入式软件开发,可能经常会使用单片机连接驱动显示屏,实现人机交互的功能,通常可选择的有 OLED 和 LCD 等,其中相关驱动代码例程网上更是数不胜数。 本文介绍的是 OLED, 常见代表有 0.96 寸的OLED,网上也有很多相关的驱动代码,但是大部分代码都是能用即可,不是特别关注驱动代码的整体架构,以下是个人整理的 OLED 驱动代码,支持配置 SPI 和 IIC 两种接口方式(目前只是测试了 SPI,IIC 有待验证)。

大橙子的博客 4290

Hash_map的使用

hash_map的用法和map是一样的,提供了 insert,size,count等操作,并且里面的元素也是以pair类型来存贮的。虽然对外部提供的函数和数据类型是一致的,但是其底层实现是完全不同的,map底层的数据结构是rb_tree而,hansh_map却是哈希表来实现的。 void main() { hash_map hm; hm.insert(pair(19, "jiang"));

Yvan Jiang的专栏 579

Intel Mobileye EyeQ Ultra:RISC-V架构的L4自动驾驶芯片解析

RISC-V作为开源指令集架构,凭借其模块化设计和可定制特性,正在重塑高性能计算领域。其精简指令集带来的确定性执行时序,特别适合自动驾驶等实时性要求严苛的场景。在异构计算架构中,RISC-V与专用加速器(如NPU、GPU)的协同设计,能显著提升能效比和算力密度。Mobileye EyeQ Ultra采用12核RISC-V集群,结合176TOPS AI加速器和5nm工艺,实现了L4级自动驾驶所需的实时感知与决策能力。这种架构为车载计算平台提供了可扩展的解决方案,同时降低了系统复杂度和BOM成本。

weixin_33245447的博客 426

unordered_map中find和count函数的区别

count函数直接放回的是一个数值,如果存在,那么返回1,反之0; find返回的是一个iterator, 直接输出iterator是会报错的,要输出的话得取出迭代器的值再输出。详情看代码 #include<iostream> #include<unordered_map> using namespace std; unordered_map<int, int> hashmap; int main(){ hashmap[1] = 9; has

qq_44879626的博客 5441

使用 Python 调用 GitHub REST API 爬取 GitHub 仓库信息(完整指南)

GitHub 提供了功能强大的 REST API,允许开发者通过编程方式访问 GitHub 上的仓库、用户、提交记录、PR(Pull Request)等信息。GitHub REST API 是 GitHub 官方提供的 API 接口,支持通过 HTTP 请求来操作 GitHub 上的资源。在本文中,我们将使用 Python 编写一个完整的爬虫程序,调用 GitHub 的 REST API 来爬取 GitHub 仓库的信息。目前 GitHub 使用的主要是 v3 版本的 REST API。

2201_76125261的博客 1110

Android AMS——AMS初始化(二)

Android AMS 也是一个系统服务,属于系统服务中的引导服务,在 startBootstrapServices() 方法中启动。这里我们就来看一下 ActivityManagerService 的启动流程。

小旭的专栏 775

深入研究HashMap

HashMap是一个用于存储Key-Value键值对的集合,每一个键值对也叫做Entry。这些个键值对(Entry)分散存储在一个数组当中,这个数组就是HashMap的主干。 HashMap数组每一个元素的初始值都是Null。 对于HashMap,我们最常使用的是两个方法:Get 和 Put。 对于HashMap,我们最常使用的是两个方法:Get 和 Put。 1.Put方法的原理 调...

lucasmaluping的专栏 238

路径规划 | 图解Informed RRT*算法(附ROS C++/Python/Matlab仿真)

Informed RRT*算法针对RRT*算法进行了采样优化,用椭圆采样代替全局均匀采样,避免了RRT*算法搜索树上产生过多冗余分支的缺陷。本文图解Informed RRT*原理,并提供ROS C++/Python/Matlab三种实现加深理解

FRIGIDWINTER的博客 7483

hashmap 的一些研究

今天面试被问到hashmap 的有关东西,结果这个很经典的结构我是了解它的原理,可是在语言中的具体实现原理还是不清楚的,仅仅是用过而已,所以回答的。。现在深入的整理一下: 在java 里面是有hashmap 的数据结构 在c++里面有map 的数据结构 介绍了java 中的hashmap 的原理 http://www.cnblogs.com/skywang12345/p/3310835

Stay hungry, stay foolish 739

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

认真研究HashMap的结点移除

**关联博文** [数据结构之Map基础入门与详解](https://janus.blog.csdn.net/article/details/53214105) [认真学习Java集合之HashMap的实现原理](https://janus.blog.csdn.net/article/details/86774652) [认真研究HashMap的读取和存放操作步骤](https://janus.blog.csdn.net/article/details/52787823) [认真研究HashMap的初始化和

小小默:进无止境 6468

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

HashMap的研究

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_33872566的博客 87

HashMap深度研究

集合中,HashMap绝对是是面试中的一大常考点,特别重要,特别是它的另两个兄弟Hashtable和ConcurrentHashMap,加起来更加让人混淆,今天我大致梳理以下: 1、HashMap 1.1、HashMap的底层实现原理 上面我们说道: 1、数组使用下标维护数据,所以查找起来比较快,但是插入和删除的话需要移动后面的数据,数据插入和删除比较慢。 2、链表使用指针来维护链表结构,查找起来比较慢,因为要从第一个结点开始查找,但是插入和删除的话不需要移动数据,只需要改变指针的指向就可以了,所以插入和

qq_45901741的博客 478

认真研究HashMap中的平衡删除

**关联博文** [数据结构之Map基础入门与详解](https://janus.blog.csdn.net/article/details/53214105) [认真学习Java集合之HashMap的实现原理](https://janus.blog.csdn.net/article/details/86774652) [认真研究HashMap的读取和存放操作步骤](https://janus.blog.csdn.net/article/details/52787823) [认真研究HashMap的初始化和

小小默:进无止境 8972

HashMap源码研究——源码一行一行的注释

看过TreeMap的源码之后,终于来到了重头戏 探究HashMap的源码 首先依旧是新建一个HashMap HashMap hashMap = new HashMap(); 让我们点进去,啊不,先看看类图 很好,继承了AbstractMap,实现了Map<K,V>, Cloneable, Serializable三个接口,关于类图的打开方式请看这篇文章 IDEA 查看 UML 类图 接下来按照惯例,先看看里面定义了什么参数 (注释过长,这里只截取参数) // 默认的HashMap中数组的长度

想着百万年薪努力的小赵 10万+

研究HashMap

      本文重点介绍HashMap。首先介绍一下什么是Map。在数组中我们是通过数组下标来对其内容索引的,而在Map中我们通过对象来对对象进行索引,用来索引的对象叫做key,其对应的对象叫做value。在下文中会有例子具体说明。      再来看看HashMap和TreeMap有什么区别。HashMap通过hashcode对其内容进行快速查找,而 TreeMap中所有的元素都保持着某种固定

Our_Zone 693

HashMap底层研究

HashMap的线程不安全,实现了Map,Cloneable, Serializable接口,以及继承了AbstractMap,可以使用null作为key,无序(存入顺序和输出顺序不一样) jdk1.8之前底层是利用数组和单向链表实现的,1.8之后增加了红黑树,当一个单向链表的长度大于8时就会使用红黑树,使其效率更高,初始容量为16,填充因子是0.75,容量翻倍是以两倍的方式capacity*2,平时无特殊要求的时候尽量选择HashMap,当遇到多线程的时候可以使用Collections.synchroni

weixin_44695057的博客 254

hashmap源码研究

概述 在官方文档中是这样描述HashMap的: Hash table based implementation of the Map interface. This implementation provides all of the optional map operations, and permits null values and the null key. (The HashMa...

weixin_30430169的博客 73

Hashmap-原理研究

Hashmap原理研究 学而不思则罔,思而不学则殆 HashMap是Java原生数据集中一个非常基本的<K,V>的数据集合

珍心 244

HashMap源码研究

HashMap是Java程序员使用频率最高的用于映射(键值对)处理的数据类型。随着JDK(Java Developmet Kit)版本的更新,JDK1.8对HashMap底层的实现进行了优化,例如引入红黑树的数据结构和扩容的优化等。本文结合JDK1.7和JDK1.8的区别,深入探讨HashMap的结构实现和功能原理。 简介 Java为数据结构中的映射定义了一个接口java.util.Map,此接口主要有四个常用的实现类,分别是HashMap、Hashtable、LinkedHashMap和TreeMa..

fangmeng1997的博客 284

认真研究HashMap中的平衡插入

本文是基于Jdk1.8,关于平衡插入这一部分涉及的内容比较多,所以从博文[认真学习Java集合之HashMap的实现原理](https://blog.csdn.net/J080624/article/details/86774652)摘取出来单独研究。 下面方法参数中的root为当前root结点,x 为新插入的结点 。xpp 为xp的parent,xppl 为xpp的left,xppr 为 xpp的right ,xp是x.parent。这个过程会涉及到红黑树的左旋和右旋。 ```java // root为

小小默:进无止境 1771
上一篇: java异常处理
下一篇: java技术与XML常见问题
romans1981
博客等级 码龄22年 8粉丝 58原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值