码迷,mamicode.com
首页 > 编程语言 > 详细

java中的HashMap解析

时间:2015-04-09 01:01:03      阅读:177      评论:0      收藏:0      [点我收藏+]

标签:

这篇文章准备从源码的角度带大家分析一下java中的hashMap的原理,在了解源码之前,我们先根据自己的理解创建一个hashMap。

先说明一下创建的具体原理是这样的,所谓hashMap,必然是用hash方法来区分不同的key值。学过hash的都知道,我们解决hash冲突的一种方法就是使用散列和桶,首先确定所在的桶号,然后在桶里面逐个查找。其实我们也可以单纯使用数组实现map,使用散列是为了获得更高的查询效率。

要写自己的hashmap前,必须说明一下两个方法,就是hashcode()和equals()方法,要在map里面判断两个key是否相等,关键在于这个两个函数的返回值一定要相等(只有一个相等是没有用的,因为hashmap会先根据hashcode()方法查找桶,然后根据equals()方法获取value)

如果我们没有复写这个两个方法,object类是根据类所在内存地址来产生hashcode的,所以一般比较是不会相同的,又正因为这样,我们在使用自己构造的类当key值的时候,有时是有必要复写这两个方法的。下面是一个例子

class myClass{
		int i = 0;
		public myClass(int i) {
			this.i = i;
		}
		@Override
		public int hashCode() {			
			return i;
		}
		
		@Override
		public boolean equals(Object obj) {			
			return obj instanceof myClass && i == ((myClass)obj).i;
		}
	}

注意上面的instanceof,我们首先要判断参数的类是否相同,这个非常重要,不过容易被忽略。(因为有可能是两个不同的类,有相同的属性,连属性值都相同,这样我们判断就会失误了)。另外我们要注意String类型重载了这两个方法,所以两个new String("aa")是相同的

在以下类中,我使用了一个arraylist来充当链,首先我们来看一个键值对类,用来保存键和值,这个是一个内部类,还有要实现hashmap必须先继承一个AbstractMap<K,V>的抽象类

import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;

public class MyHashMap<K, V> extends AbstractMap<K, V> {
	//链表长度
	final static int SIZE = 999;
        private List<K> keys = new ArrayList<K>();
        private List<V> values = new ArrayList<V>();
         /**
	 * Entry类,用于保存键值对
	 * @author Administrator
	 *
	 * @param <K>
	 * @param <V>
	 */
	static class MyEntry<K,V> implements Map.Entry<K, V>{
		private K key;
		private V value;
		
		public MyEntry(K key,V value) {
			this.key = key;
			this.value = value;
		}
		
		@Override
		public K getKey() {			
			return key;
		}

		@Override
		public V getValue() {			
			return value;
		}

		@Override
		public V setValue(V v) {		
			V oldValue = value;
			value = v;
			return oldValue;
		}
		
		@Override
		public int hashCode() {
			//使用key和value的hashcode共同构造新的hashcode
			return (key==null?0:key.hashCode())^(value==null?0:value.hashCode());
		}
		
		@Override
		public boolean equals(Object obj) {
			//注意要检查类型是否相同
			if(!(obj instanceof MyEntry)) return false;		
			MyEntry en = (MyEntry)obj; 
			//注意空值的情况
			return (key==null?en.getKey()==key:key.equals(en.getKey())) &&
					(value==null?en.getKey()==value:value.equals(en.getValue()));
		}
	}
	
	@SuppressWarnings("unchecked")
	ArrayList<MyEntry<K,V>>[] buckets = new ArrayList[SIZE];
	
	@Override
	public Set<java.util.Map.Entry<K, V>> entrySet() {
		// TODO Auto-generated method stub
		return null;
	}

}
对于上面的键值对类MyEntry,我们要实现一个接口Map.Entry,因为我们一般使用hashmap都可以获得它的Entryset,继承这个类正是为了这个做准备


接下来我们先来实现put方法

/**
     * put方法
     */
    public V put(K key,V value){
        //原值用于返回
        V oldValue = null;
        //防止越界
        int index = Math.abs(key.hashCode())%SIZE;
        //检查是否有桶,没有创建一个
        if(buckets[index]==null){
            buckets[index] = new ArrayList<MyEntry<K,V>>();
        }
        ArrayList<MyEntry<K,V>> bucket = buckets[index];
        //创建键值对对象entry
        MyEntry<K, V> pair = new MyEntry<K, V>(key, value);
        boolean found = false;
        ListIterator<MyEntry<K, V>> it = bucket.listIterator();
        //遍历桶
        while(it.hasNext()){
            MyEntry<K, V> iPair = it.next();
            //如果已经在map里面,更新
            if(iPair.getKey().equals(key)){
                oldValue = iPair.getValue();
                it.set(pair);
                values.set(keys.indexOf(key),value);        
                found = true;
                break;
            }
        }
        //不在map里面,新增
        if(!found){
            keys.add(key);
            values.add(value);
            bucket.add(pair);
        }
        return oldValue;
    }

这上面的思路应该说是非常清晰,首先查找桶,没有则新建,然后在桶里面查找key值,如果已经存在map里面了,更新,否则新增。

再来看get方法,就更加清晰了

/**
	 * get方法
	 */
	public V get(Object key){
		int index = Math.abs(key.hashCode())%SIZE;
		if(buckets[index]==null) return null;
		for(MyEntry<K, V> pair:buckets[index]){
			if(pair.getKey().equals(key)){
				return pair.getValue();
			}
		}
		return null;
	}
上面首先查找对应桶,没有返回null,如果有则在桶内遍历查找

最后再来看一下entrySet类

private class MyEntrySet extends AbstractSet<Map.Entry<K, V>>{

        @Override
        public Iterator<java.util.Map.Entry<K, V>> iterator() {
            return new Iterator<java.util.Map.Entry<K, V>>() {
                private int index = -1;
                boolean canRemove;
                @Override
                public boolean hasNext() {
                    return index<keys.size()-1;                    
                }

                @Override
                public MyEntry<K, V> next() {
                    boolean canRemove = true;
                    ++index;                    
                    return new MyEntry<K, V>(keys.get(index), values.get(index));
                }

                @Override
                public void remove() {
                    if(!canRemove){
                        throw new IllegalStateException();
                    }
                    canRemove = false;
                    keys.remove(index);
                    values.remove(index--);
                }
            };            
        }

        @Override
        public int size() {            
            return keys.size();
        }        
    }
这个内部类主要是为我们提供entry用于外部遍历使用

下面是完整代码,大家可以测试一下

package test;

import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;

public class MyHashMap<K, V> extends AbstractMap<K, V> {
    //链表长度
    final static int SIZE = 999;
    private List<K> keys = new ArrayList<K>();
    private List<V> values = new ArrayList<V>();
    
    /**
     * Entry类,用于保存键值对
     * @author Administrator
     *
     * @param <K>
     * @param <V>
     */
    static class MyEntry<K,V> implements Map.Entry<K, V>{
        private K key;
        private V value;
        
        public MyEntry(K key,V value) {
            this.key = key;
            this.value = value;
        }
        
        @Override
        public K getKey() {            
            return key;
        }

        @Override
        public V getValue() {            
            return value;
        }

        @Override
        public V setValue(V v) {        
            V oldValue = value;
            value = v;
            return oldValue;
        }
        
        @Override
        public int hashCode() {
            //使用key和value的hashcode共同构造新的hashcode
            return (key==null?0:key.hashCode())^(value==null?0:value.hashCode());
        }
        
        @Override
        public boolean equals(Object obj) {
            //注意要检查类型是否相同
            if(!(obj instanceof MyEntry)) return false;        
            MyEntry en = (MyEntry)obj; 
            //注意空值的情况
            return (key==null?en.getKey()==key:key.equals(en.getKey())) &&
                    (value==null?en.getKey()==value:value.equals(en.getValue()));
        }
    }
    
    @SuppressWarnings("unchecked")
    ArrayList<MyEntry<K,V>>[] buckets = new ArrayList[SIZE];
    
    /**
     * put方法
     */
    public V put(K key,V value){
        //原值用于返回
        V oldValue = null;
        //防止越界
        int index = Math.abs(key.hashCode())%SIZE;
        //检查是否有桶,没有创建一个
        if(buckets[index]==null){
            buckets[index] = new ArrayList<MyEntry<K,V>>();
        }
        ArrayList<MyEntry<K,V>> bucket = buckets[index];
        //创建键值对对象entry
        MyEntry<K, V> pair = new MyEntry<K, V>(key, value);
        boolean found = false;
        ListIterator<MyEntry<K, V>> it = bucket.listIterator();
        //遍历桶
        while(it.hasNext()){
            MyEntry<K, V> iPair = it.next();
            //如果已经在map里面,更新
            if(iPair.getKey().equals(key)){
                oldValue = iPair.getValue();
                it.set(pair);
                values.set(keys.indexOf(key),value);        
                found = true;
                break;
            }
        }
        //不在map里面,新增
        if(!found){
            keys.add(key);
            values.add(value);
            bucket.add(pair);
        }
        return oldValue;
    }
    
    /**
     * get方法
     */
    public V get(Object key){
        int index = Math.abs(key.hashCode())%SIZE;
        if(buckets[index]==null) return null;
        for(MyEntry<K, V> pair:buckets[index]){
            if(pair.getKey().equals(key)){
                return pair.getValue();
            }
        }
        return null;
    }
    
    private class MyEntrySet extends AbstractSet<Map.Entry<K, V>>{

        @Override
        public Iterator<java.util.Map.Entry<K, V>> iterator() {
            return new Iterator<java.util.Map.Entry<K, V>>() {
                private int index = -1;
                boolean canRemove;
                @Override
                public boolean hasNext() {
                    return index<keys.size()-1;                    
                }

                @Override
                public MyEntry<K, V> next() {
                    boolean canRemove = true;
                    ++index;                    
                    return new MyEntry<K, V>(keys.get(index), values.get(index));
                }

                @Override
                public void remove() {
                    if(!canRemove){
                        throw new IllegalStateException();
                    }
                    canRemove = false;
                    keys.remove(index);
                    values.remove(index--);
                }
            };            
        }

        @Override
        public int size() {            
            return keys.size();
        }        
    }
    
    private MyEntrySet myEntrySet = new MyEntrySet();
    @Override
    public Set<java.util.Map.Entry<K, V>> entrySet() {        
        return myEntrySet;
    }

}

OK,定义了我们自己hashmap以后,我们再来对照着看源代码,就比较容易,虽然还有些区别,但是希望加深大家的理解

首先来看get方法

/**
     * Returns the value of the mapping with the specified key.
     *
     * @param key
     *            the key.
     * @return the value of the mapping with the specified key, or {@code null}
     *         if no mapping for the specified key is found.
     */
    public V get(Object key) {
        //检查key为null
        if (key == null) {
            HashMapEntry<K, V> e = entryForNullKey;
            return e == null ? null : e.value;
        }

        // Doug Lea's supplemental secondaryHash function (inlined)
        //利用key的hashcode,计算新的hash
        int hash = key.hashCode();
        hash ^= (hash >>> 20) ^ (hash >>> 12);
        hash ^= (hash >>> 7) ^ (hash >>> 4);
        //遍历数组查找是否存在对应值
        HashMapEntry<K, V>[] tab = table;
        for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)];
                e != null; e = e.next) {
            K eKey = e.key;
            if (eKey == key || (e.hash == hash && key.equals(eKey))) {
                return e.value;
            }
        }
        return null;
    }

用源代码跟我们写的代码比较,发现也是先处理null值,源码中使用了一个特定的对象来代表key为Null的entry

然后是计算新的hash,这个怎么计算我们不理它,只要知道为了hash更加完美,我们需要根据key的hashcode重新一次hash值

然后及时遍历查找对应value


接下来看put方法

/**
     * Maps the specified key to the specified value.
     *
     * @param key
     *            the key.
     * @param value
     *            the value.
     * @return the value of any previous mapping with the specified key or
     *         {@code null} if there was no such mapping.
     */
    @Override public V put(K key, V value) {
        //如果新增的key为null,直接返回新生成的一个特定对象
        if (key == null) {
            return putValueForNullKey(value);
        }
        //重新计算hash值
        int hash = secondaryHash(key.hashCode());
        HashMapEntry<K, V>[] tab = table;
        int index = hash & (tab.length - 1);
        //遍历,如果存在就更新
        for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) {
            if (e.hash == hash && key.equals(e.key)) {
                preModify(e);
                V oldValue = e.value;
                e.value = value;
                return oldValue;
            }
        }

        // No entry for (non-null) key is present; create one
        modCount++;
        if (size++ > threshold) {
            tab = doubleCapacity();
            index = hash & (tab.length - 1);
        }
        //没有就新增
        addNewEntry(key, value, hash, index);
        return null;
    }
    /**
    *为控制生产一个特定对象
    */
    private V putValueForNullKey(V value) {
        HashMapEntry<K, V> entry = entryForNullKey;
        if (entry == null) {
            addNewEntryForNullKey(value);
            size++;
            modCount++;
            return null;
        } else {
            preModify(entry);
            V oldValue = entry.value;
            entry.value = value;
            return oldValue;
        }
    }
对比我们的代码来看,思路差不多,就是处理null值的时候有不同
最后来看我们的entrySet
private final class EntrySet extends AbstractSet<Entry<K, V>> {
        public Iterator<Entry<K, V>> iterator() {
            return newEntryIterator();
        }
        public boolean contains(Object o) {
            if (!(o instanceof Entry))
                return false;
            Entry<?, ?> e = (Entry<?, ?>) o;
            return containsMapping(e.getKey(), e.getValue());
        }
        public boolean remove(Object o) {
            if (!(o instanceof Entry))
                return false;
            Entry<?, ?> e = (Entry<?, ?>)o;
            return removeMapping(e.getKey(), e.getValue());
        }
        public int size() {
            return size;
        }
        public boolean isEmpty() {
            return size == 0;
        }
        public void clear() {
            HashMap.this.clear();
        }
    }

必须实现的方法有对应的实现,其中size是另外记录的一个变量,用来记录数据条数

这个必须结合iterator一起看,查找源代码以后,发现对应的是这个class

private final class EntryIterator extends HashIterator
            implements Iterator<Entry<K, V>> {
        public Entry<K, V> next() { return nextEntry(); }
    }
继承自HashIterator
private abstract class HashIterator {
        int nextIndex;
        HashMapEntry<K, V> nextEntry = entryForNullKey;
        HashMapEntry<K, V> lastEntryReturned;
        int expectedModCount = modCount;

        HashIterator() {
            if (nextEntry == null) {
                HashMapEntry<K, V>[] tab = table;
                HashMapEntry<K, V> next = null;
                while (next == null && nextIndex < tab.length) {
                    next = tab[nextIndex++];
                }
                nextEntry = next;
            }
        }

        public boolean hasNext() {
            return nextEntry != null;
        }

        HashMapEntry<K, V> nextEntry() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            if (nextEntry == null)
                throw new NoSuchElementException();

            HashMapEntry<K, V> entryToReturn = nextEntry;
            HashMapEntry<K, V>[] tab = table;
            HashMapEntry<K, V> next = entryToReturn.next;
            while (next == null && nextIndex < tab.length) {
                next = tab[nextIndex++];
            }
            nextEntry = next;
            return lastEntryReturned = entryToReturn;
        }

        public void remove() {
            if (lastEntryReturned == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            HashMap.this.remove(lastEntryReturned.key);
            lastEntryReturned = null;
            expectedModCount = modCount;
        }
    }



java中的HashMap解析

标签:

原文地址:http://blog.csdn.net/crazy__chen/article/details/44947405

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!