zoukankan      html  css  js  c++  java
  • JDK源码-HashMap

    1,Map:映射表数据结构,通过key-value完成映射。HashMap的子实现主要包括:HashMap、LinkedHashMap、TreeMap、WeakHashMap、ConcurrentHashMap、IdentityHashMap。以下总结摘录自《Thingking In Java》
        -1,HashMap:使用Map集合的默认选择。因为HashMap对速度进行了优化。HashMap是Map基于散列表的实现,并取代了Hashtable。插入和查询的效率相对固定。可以通过构造器设置容量和负载因子以调整容器的性能。
        -2,LinkedHashMap:类似于HashMap,但是迭代遍历的时候,取得的顺序是其插入顺序。只比HashMap慢一点,但是在迭代访问时反而更快,因为其使用链表维护内部次序。
        -3,TreeMap:基于红黑树的实现(大学毕业面试百度的时候,被问道红黑树,然后被鄙视了。)。查看键值对时,会被排序(次序由Comparable或Comparator决定)。TreeMap的特点是得到的结果是排序后的。TreeMap是唯一一个带有subMap方法的Map,可以返回一个子Map。
        -4,WeakHashMap:弱键映射,允许释放映射所指向的对象。这是为了解决特殊问题设置的,比如希望对象能够被垃圾回收机制尽快回收。
        -5,ConcurrentHashMap:一种线程安全的Map,不涉及到同步加锁。
        -6,IdentityHashMap:使用==代替equals对key值进行比较。
    /**
    *
    * @(#) Main.java
    * @Package com.map
    *
    * Copyright © JING Corporation. All rights reserved.
    *
    */
     
    package com.map;
     
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.LinkedHashMap;
    import java.util.Map;
    import java.util.Set;
    import java.util.TreeMap;
     
    /**
    * 类描述:Run Config设置vm参数:-Xms128M -Xmx512M -XX:PermSize=64M -XX:MaxPermSize=128M
    *
    * @author: Jing History: Jan 21, 2015 10:14:20 AM Jing Created.
    *
    */
    public class Main {
     
    public static void main(String[] args) {
    // HashMap 插入效率
    Map<Integer, Integer> hashMap = new HashMap<Integer, Integer>();
    long startTime = System.currentTimeMillis();
    addData(hashMap);
    long endTime = System.currentTimeMillis();
    System.out.println("HashMap插入1000000条数据,用时: " + (endTime - startTime));// 272
     
    startTime = System.currentTimeMillis();
    iterMap(hashMap);
    endTime = System.currentTimeMillis();
    System.out.println("HashMap遍历1000000条数据,用时: " + (endTime - startTime));// 84
    // System.out.println(hashMap.toString());
    // LinkedHashMap
    Map<Integer, Integer> linkedHashMap = new LinkedHashMap<Integer, Integer>();
    startTime = System.currentTimeMillis();
    addData(linkedHashMap);
    endTime = System.currentTimeMillis();
    System.out.println("LinkedHashMap插入1000000条数据,用时: "
    + (endTime - startTime));// 551 LinkedHashMap HashMap
    // 插入和遍历的数据越大,数值的差异越明显
    startTime = System.currentTimeMillis();
    iterMap(linkedHashMap);
    endTime = System.currentTimeMillis();
    System.out.println("LinkedHashMap遍历1000000条数据,用时: "
    + (endTime - startTime));// 85 LinkedHashMap HashMap
    // 插入和遍历的数据越大,数值的越接近
    // System.out.println(linkedHashMap.toString());
     
    Map<Integer, Integer> treeMap = new TreeMap<Integer, Integer>();
    startTime = System.currentTimeMillis();
    addData(treeMap);
    endTime = System.currentTimeMillis();
    System.out.println(" TreeMap插入1000000条数据,用时: " + (endTime - startTime));// 491
    startTime = System.currentTimeMillis();
    iterMap(treeMap);
    endTime = System.currentTimeMillis();
    System.out.println(" TreeMap遍历1000000条数据,用时: " + (endTime - startTime));// 538
    // System.out.println(treeMap.toString());
    }
     
    /**
    *
    * 方法说明:向Map中添加数据
    *
    * Author: Jing Create Date: Jan 21, 2015 10:40:18 AM
    */
    static void addData(Map<Integer, Integer> map) {
    for (int i = 0; i <= 1000000; i++) {
    int value = (int) (Math.random() * i);
    map.put(i, value);
    }
     
    }
     
    /**
    *
    * 方法说明:遍历Map
    *
    * Author: Jing Create Date: Jan 21, 2015 10:41:00 AM
    */
    static void iterMap(Map<Integer, Integer> map) {
     
    Set<Integer> keySet = map.keySet();
    Iterator<Integer> iter = keySet.iterator();
    while (iter.hasNext()) {
     
    int key = iter.next();
    map.get(key);
    }
     
    }
    }
    2,HashMap:
         -1,基于哈希表的Map接口实现,并允许null值和null键。此类不保证映射顺序,特别是不保证该顺序恒久不变。
        -2,HashMap的实例有两个参数影响性能分布:初始容量和加载因子。容量是哈希表中桶的数量,出事容量只是哈希表在初始化是创建的容量。加载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子和当前容量的乘时,则要对哈希表进行rehash操作,从而哈希表将拥有之前两倍的桶数。即,哈希表中存储的数据量=加载因子 * 当前容量(桶数)。
        所以不能将初始容量设置的太高,或将加载因子设置的太低。默认加载因子为0.75。加载因子过小会造成空间的浪费,加载因子过高会造成哈希表中冲突的增加。
        此处需要自己阅读下哈希表的数据结构。
    3,HashMap继承关系:
       
    4,Map接口:Map接口虽然不继承Collection接口,但是仍然可视为一种Collection视图。Map接口定义了Map子集的所有共用特性方法,并定义了内部类Map.Entry类。Map.Entry存储了Map的key-value对,该类提供了getValue和getKey方法。
    5,AbstractMap:实现Map接口的主要实现,方便开发人员重写Map接口。
    6,HashMap的静态变量:
        -1,初始化容量
    static final int DEFAULT_INITIAL_CAPACITY = 16;
        -2,最大容量: 2的30次方
    static final int MAXIMUM_CAPACITY = 1 << 30;
        -3,默认加载因子
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    7,HashMap的成员变量:
        -1,Entry[] table:存储Entry键值对对象的数组。相当于hash算法中的初始数组。
    transient Entry[] table;
        -2, int size:key-value键值对的数量。
    transient int size;
        -3,int threshold:需要下次扩展的长度,结果为capacity * load
    int threshold;
         -4,int modCount:HashMap结构化修改次数
    transient volatile int modCount;
        -5,entrySet
    // Views
     
    private transient Set<Map.Entry<K,V>> entrySet = null;
    8,主要方法:
        -1,put(K key, V value):
    public V put(K key, V value) {
    if (key == null)
    return putForNullKey(value);//如果key为空,执行putForNullKey方法
    int hash = hash(key.hashCode());//获取到key对应的hashCode值
    int i = indexFor(hash, table.length);//求取对应hashCode值在table中的位置
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {//判断对应算出哈希表中的位置是否存在元素,如果存在元素,判断该Entry链。如果hash值相等,并且key相同,则替换值
    Object k;
    if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
    V oldValue = e.value;
    e.value = value;
    e.recordAccess(this);
    return oldValue;
    }
    }
     
    modCount++;//结构化修改计数器加1
    addEntry(hash, key, value, i);//在对应哈希表的位置中增加Entry对象。
    return null;
    }
    此处的for循环判断Entry链,即HashMap是使用哈希表的数据结构存放,但是对应哈希值相同的元素,使用链表在该位置存放。我们可以看Entry的源码:
    static class Entry<K,V> implements Map.Entry<K,V> {
    final K key;
    V value;
    Entry<K,V> next;//链表,标识同一哈希值的下一实体
    final int hash;

    根据put方法,可以查看对应的几个方法。
        putForNullKey:放置null Key。
    private V putForNullKey(V value) {
    for (Entry<K,V> e = table[0]; e != null; e = e.next) {
    if (e.key == null) {//查找table中的null key替换value
    V oldValue = e.value;
    e.value = value;
    e.recordAccess(this);
    return oldValue;
    }
    }
    modCount++;
    addEntry(0, null, value, 0);
    return null;
    }
    addEntry方法:在对应table数组的buketIndex位置上,增加新的Entry对象,并使新Entry对象的next指向原有Entry对象。
    void addEntry(int hash, K key, V value, int bucketIndex) {
    Entry<K,V> e = table[bucketIndex];
    table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
    if (size++ >= threshold)//存储元素加1,并在加1后判断是否超过了阀值,阀值在初始化HashMap时初始化。
    resize(2 * table.length);
    }
    参考Entry对象的构造方法,很容易理解此处的代码:
    Entry(int h, K k, V v, Entry<K,V> n) {
    value = v;
    next = n;
    key = k;
    hash = h;
    }
    -2,get(Object k):
    public V get(Object key) {
    if (key == null)
    return getForNullKey();
    int hash = hash(key.hashCode());//获取哈希值
    for (Entry<K,V> e = table[indexFor(hash, table.length)];//获取对应哈希值的Entry链,遍历Entry链,查找到对应key的Entry对像,返回对应Entry的value。
    e != null;
    e = e.next) {
    Object k;
    if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
    return e.value;
    }
    return null;
    }
    -3,remove(Object key):
    public V remove(Object key) {
    Entry<K,V> e = removeEntryForKey(key);
    return (e == null ? null : e.value);
    }
    final Entry<K,V> removeEntryForKey(Object key) {
    int hash = (key == null) ? 0 : hash(key.hashCode());//获取hash值
    int i = indexFor(hash, table.length);//hash在哈希表中的位置
    Entry<K,V> prev = table[i];//获取对应值的Entry链的首元素
    Entry<K,V> e = prev;
     
    while (e != null) {
    Entry<K,V> next = e.next;//遍历Entry链,查找值,从Entry链中移除
    Object k;
    if (e.hash == hash &&
    ((k = e.key) == key || (key != null && key.equals(k)))) {
    modCount++;
    size--;
    if (prev == e)
    table[i] = next;
    else
    prev.next = next;
    e.recordRemoval(this);
    return e;
    }
    prev = e;
    e = next;
    }
     
    return e;
    }
    -4,HashIterator:HashMap对应的迭代器
    private abstract class HashIterator<E> implements Iterator<E> {
    Entry<K,V> next; // next entry to return
    int expectedModCount; // For fast-fail
    int index; // current slot
    Entry<K,V> current; // current entry
     
    HashIterator() {
    expectedModCount = modCount;
    if (size > 0) { // advance to first entry
    Entry[] t = table;
    while (index < t.length && (next = t[index++]) == null)
    ;
    }
    }
     
    public final boolean hasNext() {
    return next != null;
    }
     
    final Entry<K,V> nextEntry() {
    if (modCount != expectedModCount)
    throw new ConcurrentModificationException();
    Entry<K,V> e = next;
    if (e == null)
    throw new NoSuchElementException();
     
    if ((next = e.next) == null) {
    Entry[] t = table;
    while (index < t.length && (next = t[index++]) == null)
    ;
    }
    current = e;
    return 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;
    }
     
    }
    -5,keySet
    private final class KeySet extends AbstractSet<K> {
    public Iterator<K> 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();
    }
    }
    -6,三个迭代器
    private final class ValueIterator extends HashIterator<V> {
    public V next() {
    return nextEntry().value;
    }
    }
     
    private final class KeyIterator extends HashIterator<K> {
    public K next() {
    return nextEntry().getKey();
    }
    }
     
    private final class EntryIterator extends HashIterator<Map.Entry<K,V>> {
    public Map.Entry<K,V> next() {
    return nextEntry();
    }
    }
    9,HashMap子类,LinekedHashMap
    LinekedHashMap使用链表实现其底层结构。
    private transient Entry<K,V> header;
    基于HashMap.Entry对象,其定义了链表的Entry对象:
    private static class Entry<K,V> extends HashMap.Entry<K,V> {
    // These fields comprise the doubly linked list used for iteration.
    Entry<K,V> before, after;
     
    Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
    super(hash, key, value, next);
    }
     
    /**
    * Removes this entry from the linked list.
    */
    private void remove() {
    before.after = after;
    after.before = before;
    }
     
    /**
    * Inserts this entry before the specified existing entry in the list.
    */
    private void addBefore(Entry<K,V> existingEntry) {
    after = existingEntry;
    before = existingEntry.before;
    before.after = this;
    after.before = this;
    }
    删除和新增操作等,与LinkedList实现方式基本保持一致。


    欢迎转载,但转载请注明原文链接[博客园: http://www.cnblogs.com/jingLongJun/]
    [CSDN博客:http://blog.csdn.net/mergades]。
    如相关博文涉及到版权问题,请联系本人。
  • 相关阅读:
    Final-阶段站立会议5
    Debug阶段成员贡献分
    每周例行报告——第九周
    beta发布简评
    简易四则运算生成程序——批量出题
    每周例行报告——第八周
    每周例行报告——第七周
    每周例行报告——第六周
    课堂作业:alpha发布点评
    “四则运算生成程序——GUI支持和部分功能改进”链接
  • 原文地址:https://www.cnblogs.com/jingLongJun/p/4491053.html
Copyright © 2011-2022 走看看