JDK1.7 hashMap的简单实现

来源:互联网 发布:js 寻找字符串 编辑:程序博客网 时间:2024/06/03 23:44

HashMap是我们在日常的开发中最长见的类之一,对于HashMap的使用在这里就不用过多的解释了。我们来看看HashMap的简单的实现原理。
首先在jdk1.7之前的版本中,HashMap的实现的都是依靠的,(数组 + 链表)的数据结构。在jdk1.8中采用的是(数组 + 红黑树)的数据结构。
对于HashMap的理解,首先我们可以简单的自己写一个HashMap的实现来加深理解,那么就先不考虑jdk1.8,用(数组 + 链表)的形式来简单的实现。

1.定义一个ICustomMap接口方法有put(), get()方法,并且定义一个Entry内部接口
2.一定一个ICustomHashMap的类实现ICustomMap,实现put()和get()方法
uml图如下:
这里写图片描述
具体的代码如下:

public interface ICustomMap<K, V> {    K put(K key, V value);    V get(K key);    interface Entry<K, V> {        K getKey();        V getValue();    }}public class ICustomHashMap<K, V> implements ICustomMap {    @Override    public Object put(Object key, Object value) {        return null;    }    @Override    public Object get(Object key) {        return null;    }}

那么就开始实现put()方法。

实现的大致思路是:
对(数组 + 链表)的结构可以这样理解。一堆数据我们按照某个规律分为几个桶,就是分为几个数组,那么分到一个通的,我们可以称之为hash冲突,如何解决hash冲突呢,hashmap的操作是把产生hash冲突的数据按照链表的形式存储起来(有点像ArrayList + linkList的结合)。
那么就开始实现put的方法:

@Override    public Object put(Object key, Object value) {        int hash = key.hashCode();        int i = indexFor(hash);        /**         *         * 遍历这个数组上的没一个节点,观察是否有相同的,如果有相同的节点那么就替换掉         * 判断两个对象是否相等(hashcode是否相等 + equals是否相同)         */        for (Entry<K, V> e = table[i]; e != null ; e = e.next) {            Object k;            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {                e.value = (V) value;                return key;            }        }        table[i] = new Entry(key, value, table[i], hash);        return null;    }static class Entry<K,V> implements ICustomMap.Entry<K,V> {        K key;        V value;        Entry<K, V> next;        int hash;        Entry(K k, V v, Entry<K, V> e, int h) {            key = k;            value = v;            next = e;            hash = h;        }        @Override        public K getKey() {            return key;        }        @Override        public V getValue() {            return value;        }    }

在Entry的实现类中,放了4个值,key,value,上一个节点next,和确定唯一性的hash值。
每put的一个新的元素进来的时候,
(1)先求在哪个一个数组下标下,indexFor(),
(2)然后判断是否重复
(3)把值放入下标中,并且把next指向前一个值形成链表

其实这种的方式是有一定的随机性的,如果hash冲突特别的多,hasmap就会退化成一个线性结构。
在jdk1.7的源码中,对此没有给出什么好的解决方法,在jdk1.8中个出的解决方案是,不采用链表,而是采用红黑树来存储hash冲突的数据,在查找的时候,时间复杂度从原先的O(n)将为O(logn)。我们会在下节介绍在jdk1.8中的hashmap的实现。
下面附上hashmap1.7 api的方法,已经自己对这个方法的理解:
hashMap有4个构造方法:

HashMap()HashMap(int initialCapacity)HashMap(int initialCapacity, float loadFactor)HashMap(Map<? extends K,? extends V> m) 

最常用的是第一个什么都不传入,默认的数组容量大小为16,负载因子为0.75,负载因子的作用是在数组扩容时体现的,当一个hashmap的hash冲突过多的时候,如果数组不扩容,那么可能的情况就是数据线性存放,那么在查找的时候时间复杂度为O(n),所以当haspmap中的容量 > 数组容量(默认为16) * 负载因子(0.75),数组扩容2倍

void clear() 清除map中所有的元素Object clone()  返回hashmap浅拷贝的实例,该方法并非map中的方法。boolean containsKey(Object key) 是否包括某个keyboolean containsValue(Object value) 是否包括某个valueSet<Map.entry<K,V>> entrySet() 返回Map映射中的映射集合。此方法用与遍历mapV get(Object key) 用key查找值,返回值的范性数据boolean isEmpty() 是否为空 空为true 非空为 falseset<K> keySet() key的set集合K put(K key, V value) 存放键值对,返回键void putAll(Map<? extends K,? extends V> m) 存放一个map映射V remove(Object K) 移除一个键值对int size() 返回hashmap的键值对个数collection<V> values() 返回map的值的集合。