数据结构之哈希表实现浅析

时间:2014-05-12 01:46:52   收藏:0   阅读:413

看了下JAVA里面有HashMap、Hashtable、HashSet三种hash集合的实现源码,这里总结下,理解错误的地方还望指正

HashMap和Hashtable的区别

HashSet和HashMap、Hashtable的区别

HashMap和Hashtable的实现原理

HashMap的简化实现MyHashMap

 

HashMap和Hashtable的区别

  1. 两者最主要的区别在于Hashtable是线程安全,而HashMap则非线程安全
    Hashtable的实现方法里面都添加了synchronized关键字来确保线程同步,因此相对而言HashMap性能会高一些,我们平时使用时若无特殊需求建议使用HashMap,在多线程环境下若使用HashMap需要使用Collections.synchronizedMap()方法来获取一个线程安全的集合(Collections.synchronizedMap()实现原理是Collections定义了一个SynchronizedMap的内部类,这个类实现了Map接口,在调用方法时使用synchronized来保证线程同步,当然了实际上操作的还是我们传入的HashMap实例,简单的说就是Collections.synchronizedMap()方法帮我们在操作HashMap时自动添加了synchronized来实现线程同步,类似的其它Collections.synchronizedXX方法也是类似原理
  2. HashMap可以使用null作为key,而Hashtable则不允许null作为key
    虽说HashMap支持null值作为key,不过建议还是尽量避免这样使用,因为一旦不小心使用了,若因此引发一些问题,排查起来很是费事
    HashMap以null作为key时,总是存储在table数组的第一个节点上
  3. HashMap是对Map接口的实现,HashTable实现了Map接口和Dictionary抽象类
  4. HashMap的初始容量为16,Hashtable初始容量为11,两者的填充因子默认都是0.75
    HashMap扩容时是当前容量翻倍即:capacity*2,Hashtable扩容时是容量翻倍+1即:capacity*2+1
  5. 两者计算hash的方法不同
    Hashtable计算hash是直接使用key的hashcode对table数组的长度直接进行取模
    bubuko.com,布布扣
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    bubuko.com,布布扣

    HashMap计算hash对key的hashcode进行了二次hash,以获得更好的散列值,然后对table数组长度取摸

    bubuko.com,布布扣
    static int hash(int h) {
            // This function ensures that hashCodes that differ only by
            // constant multiples at each bit position have a bounded
            // number of collisions (approximately 8 at default load factor).
            h ^= (h >>> 20) ^ (h >>> 12);
            return h ^ (h >>> 7) ^ (h >>> 4);
        }
    
     static int indexFor(int h, int length) {
            return h & (length-1);
        }
    bubuko.com,布布扣

     

  6. HashMap和Hashtable的底层实现都是数组+链表结构实现

HashSet和HashMap、Hashtable的区别

除开HashMap和Hashtable外,还有一个hash集合HashSet,有所区别的是HashSet不是key value结构,仅仅是存储不重复的元素,相当于简化版的HashMap,只是包含HashMap中的key而已

通过查看源码也证实了这一点,HashSet内部就是使用HashMap实现,只不过HashSet里面的HashMap所有的value都是同一个Object而已,因此HashSet也是非线程安全的,至于HashSet和Hashtable的区别,HashSet就是个简化的HashMap的,所以你懂的
下面是HashSet几个主要方法的实现

bubuko.com,布布扣
  private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
public HashSet() { map = new HashMap<E,Object>(); } public boolean contains(Object o) { return map.containsKey(o); } public boolean add(E e) { return map.put(e, PRESENT)==null; } public boolean add(E e) { return map.put(e, PRESENT)==null; } public boolean remove(Object o) { return map.remove(o)==PRESENT; } public void clear() { map.clear(); }
bubuko.com,布布扣

 

HashMap和Hashtable的实现原理

HashMap和Hashtable的底层实现都是数组+链表结构实现的,这点上完全一致

添加、删除、获取元素时都是先计算hash,根据hash和table.length计算index也就是table数组的下标,然后进行相应操作,下面以HashMap为例说明下它的简单实现

bubuko.com,布布扣
  /**
     * HashMap的默认初始容量 必须为2的n次幂
     */
    static final int DEFAULT_INITIAL_CAPACITY = 16;

    /**
     * HashMap的最大容量,可以认为是int的最大值    
     */
    static final int MAXIMUM_CAPACITY = 1 << 30;

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

    /**
     * HashMap用来存储数据的数组
     */
    transient Entry[] table;
bubuko.com,布布扣

HashMap的简化实现MyHashMap

为了加深理解,我个人实现了一个简化版本的HashMap,注意哦,仅仅是简化版的功能并不完善,仅供参考

bubuko.com,布布扣
package cn.lzrabbit.structure;

/**
 * Created by rabbit on 14-5-4.
 */
public class MyHashMap {

    //默认初始化大小 16
    private static final int DEFAULT_INITIAL_CAPACITY = 16;
    //默认负载因子 0.75
    private static final float DEFAULT_LOAD_FACTOR = 0.75f;

    //临界值
    private int threshold;

    //元素个数
    private int size;

    //扩容次数
    private int resize;

    private HashEntry[] table;

    public MyHashMap() {
        table = new HashEntry[DEFAULT_INITIAL_CAPACITY];
        threshold = (int) (DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
        size = 0;
    }

    private int index(Object key) {
        //根据key的hashcode和table长度取模计算key在table中的位置
        return key.hashCode() % table.length;
    }

    public void put(Object key, Object value) {
        //key为null时需要特殊处理,为简化实现忽略null值
        if (key == null) return;
        int index = index(key);

        //遍历index位置的entry,若找到重复key则更新对应entry的值,然后返回
        HashEntry entry = table[index];
        while (entry != null) {
            if (entry.getKey().hashCode() == key.hashCode() && (entry.getKey() == key || entry.getKey().equals(key))) {
                entry.setValue(value);
                return;
            }
            entry = entry.getNext();
        }
        //若index位置没有entry或者未找到重复的key,则将新key添加到table的index位置
        add(index, key, value);
    }

    private void add(int index, Object key, Object value) {
        //将新的entry放到table的index位置第一个,若原来有值则以链表形式存放
        HashEntry entry = new HashEntry(key, value, table[index]);
        table[index] = entry;
        //判断size是否达到临界值,若已达到则进行扩容,将table的capacicy翻倍
        if (size++ >= threshold) {
            resize(table.length * 2);
        }
    }

    private void resize(int capacity) {
        if (capacity <= table.length) return;

        HashEntry[] newTable = new HashEntry[capacity];
        //遍历原table,将每个entry都重新计算hash放入newTable中
        for (int i = 0; i < table.length; i++) {
            HashEntry old = table[i];
            while (old != null) {
                HashEntry next = old.getNext();
                int index = index(old.getKey());
                old.setNext(newTable[index]);
                newTable[index] = old;
                old = next;
            }
        }
        //用newTable替table
        table = newTable;
        //修改临界值
        threshold = (int) (table.length * DEFAULT_LOAD_FACTOR);
        resize++;
    }

    public Object get(Object key) {
        //这里简化处理,忽略null值
        if (key == null) return null;
        HashEntry entry = getEntry(key);
        return entry == null ? null : entry.getValue();
    }

    public HashEntry getEntry(Object key) {
        HashEntry entry = table[index(key)];
        while (entry != null) {
            if (entry.getKey().hashCode() == key.hashCode() && (entry.getKey() == key || entry.getKey().equals(key))) {
                return entry;
            }
            entry = entry.getNext();
        }
        return null;
    }

    public void remove(Object key) {
        if (key == null) return;
        int index = index(key);
        HashEntry pre = null;
        HashEntry entry = table[index];
        while (entry != null) {
            if (entry.getKey().hashCode() == key.hashCode() && (entry.getKey() == key || entry.getKey().equals(key))) {
                if (pre == null) table[index] = entry.getNext();
                else pre.setNext(entry.getNext());
                //如果成功找到并删除,修改size
                size--;
                return;
            }
            pre = entry;
            entry = entry.getNext();
        }
    }

    public boolean containsKey(Object key) {
        if (key == null) return false;
        return getEntry(key) != null;
    }

    public int size() {
        return this.size;
    }

    public void clear() {
        for (int i = 0; i < table.length; i++) {
            table[i] = null;
        }
        this.size = 0;
    }


    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(String.format("size:%s capacity:%s resize:%s\n\n", size, table.length, resize));
        for (HashEntry entry : table) {
            while (entry != null) {
                sb.append(entry.getKey() + ":" + entry.getValue() + "\n");
                entry = entry.getNext();
            }
        }
        return sb.toString();
    }
}

class HashEntry {
    private final Object key;
    private Object value;
    private HashEntry next;

    public HashEntry(Object key, Object value, HashEntry next) {
        this.key = key;
        this.value = value;
        this.next = next;
    }

    public Object getKey() {
        return key;
    }

    public Object getValue() {
        return value;
    }

    public void setValue(Object value) {
        this.value = value;
    }

    public HashEntry getNext() {
        return next;
    }

    public void setNext(HashEntry next) {
        this.next = next;
    }
}
MyHashMap

 

数据结构之哈希表实现浅析,布布扣,bubuko.com

评论(0
© 2014 mamicode.com 版权所有 京ICP备13008772号-2  联系我们:gaon5@hotmail.com
迷上了代码!