Java集合(18)——HashTable源码解析

时间:2022-03-14 17:52:39

类图

Java集合(18)——HashTable源码解析

官方文档

Java集合(18)——HashTable源码解析

(1) This class implements a hash table, which maps keys to values. Any non-null object can be used as a key or as a value.
(2) To successfully store and retrieve objects from a hashtable, the objects used as keys must implement the hashCode method and the equals method.
(3) An instance of Hashtable has two parameters that affect its performance: initial capacity and load factor. The capacity is the number of buckets in the hash table, and the initial capacity is simply the capacity at the time the hash table is created. Note that the hash table is open: in the case of a “hash collision”, a single bucket stores multiple entries, which must be searched sequentially. The load factor is a measure of how full the hash table is allowed to get before its capacity is automatically increased. The initial capacity and load factor parameters are merely hints to the implementation. The exact details as to when and whether the rehash method is invoked are implementation-dependent.
(4) Generally, the default load factor (.75) offers a good tradeoff between time and space costs. Higher values decrease the space overhead but increase the time cost to look up an entry (which is reflected in most Hashtable operations, including get and put).
(5) The initial capacity controls a tradeoff between wasted space and the need for rehash operations, which are time-consuming. No rehash operations will ever occur if the initial capacity is greater than the maximum number of entries the Hashtable will contain divided by its load factor. However, setting the initial capacity too high can waste space.
(6) If many entries are to be made into a Hashtable, creating it with a sufficiently large capacity may allow the entries to be inserted more efficiently than letting it perform automatic rehashing as needed to grow the table.
(7) This example creates a hashtable of numbers. It uses the names of the numbers as keys:

Java集合(18)——HashTable源码解析

(1) hashtable不允许存储空键和空值
(2) 要成功存储和检索哈希表中的对象,用作键的对象必须实现hashCode方法和equals方法。
(3) 哈希表的实例有两个影响其性能的参数:初始容量和负载因子。capacity是哈希表中的桶数,初始容量只是创建哈希表时的容量。请注意,在“散列冲突”的情况下,单个桶存储多个元素(多个元素以链表形式存储),必须按顺序搜索这些元素。

成员变量

Java集合(18)——HashTable源码解析

  1. private transient Entry<?,?>[] table; //保存key-value的数组,hashtable与hashmap一样采用单链表法解决冲突,每个Entry实际上是一个单链表
  2. private transient int count; //键值对的数量
  3. private int threshold; //集合的阈值,其值等于集合容量*加载因子
  4. private float loadFactor; //加载因子
  5. private transient int modCount = 0; //hashtable结构上修改的次数,在fail-fast机制中起着关键作用
  6. private static final long serialVersionUID = 1421746759512286392L; //序列版本号
  7. private transient volatile Set<K> keySet; //Hashtable的“key的集合”。它是一个Set,没有重复元素
  8. private transient volatile Set<Map.Entry<K,V>> entrySet; // Hashtable的“key-value的集合”。它是一个Set,没有重复元素
  9. private transient volatile Collection<V> values; // Hashtable的“key-value的集合”。它是一个Collection,可以有重复元素

成员方法

Java集合(18)——HashTable源码解析

Java集合(18)——HashTable源码解析

成员方法源码解析

1. public Hashtable(int initialCapacity, float loadFactor)构造函数

    public Hashtable(int initialCapacity, float loadFactor) {

        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal Load: "+loadFactor);
        if (initialCapacity==0)
            initialCapacity = 1;
        this.loadFactor = loadFactor;
        table = new Entry<?,?>[initialCapacity];
        threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
    }

源码解析:

  • 功能:带有两个参数的构造方法,参数:初始容量和负载因子
  • 源码思路:
    • (1) 如果初始容量小于0,则抛出异常
    • (2) 如果负载因子小于等于0或者负载因子不是float类型,则抛出异常
    • (3) 如果初始容量等于0,则将初始容量置为1
    • (4) 将当前的负载因子的值设置为参数的负载因子值
    • (5) 创建一个初始容量为IinitialCapaticy的Entry,将创建的对象赋值给当前集合存储键值的数组、
    • (6) 设置当前集合的阈值,该值等于最小值(初始容量*负载因子,集合中元素个数+1)

2. public Hashtable(int initialCapacity)构造方法

    public Hashtable(int initialCapacity) {
        this(initialCapacity, 0.75f);
    }

源码解析:

  • 功能:带有一个参数的构造方法,参数:初始容量
  • 源码思路:
    • 调用该类中的带有两个参数的构造方法,其中第一个参数为传递进来的参数,第二个负载因子设置为默认值0.75

3. public Hashtable()方法

    public Hashtable() {
        this(11, 0.75f);
    }

源码解析:

  • 功能:无参构造方法
  • 源码思路:
    • 调用带有两个参数的构造方法,其中两个参数的值都是hashtable的默认初始容量和负载因子,初始容量为11,负载因子为0.75。注意这里面的初始容量与hashmap的初始容量不同,hashmap的值为16

4. public Hashtable(Map<? extends K, ? extends V> t)方法

    public Hashtable(Map<? extends K, ? extends V> t) {
        this(Math.max(2*t.size(), 11), 0.75f);
        putAll(t);
    }

源码解析:

  • 功能:带一个参数的构造方法,参数为Map类型的集合t
  • 源码思路:
    • (1)调用两个参数的构造方法,其中初始容量设置为2倍的集合t元素个数和11之间的最大值,负载因子为0.75
    • (2)需要将集合t中的元素全部放入新的hashtable中,实现该操作的方法是putAll()

5. public synchronized int size()方法

    public synchronized int size() {
        return count;
    }

源码解析:

  • 功能:返回集合中元素的个数
  • 源码思路:
    • 因为集合类中有成员变量count,其值为集合中元素的个数,因此该方法直接返回该成员变量即可

6. public synchronized boolean isEmpty()方法

    public synchronized boolean isEmpty() {
        return count == 0;
    }

源码解析:

  • 功能:判断集合是否为空
  • 源码思路:
    • 直接判断成员变量count和0之间的关系即可

7. public synchronized Enumeration<K> keys()方法

    public synchronized Enumeration<K> keys() {
        return this.<K>getEnumeration(KEYS);
    }

源码解析:

  • 功能:返回该集合的KEYS的枚举(该方法理解不透彻)

8. public synchronized <V> elements()方法

    public synchronized  <V> elements() {
        return this.<V>getEnumeration(VALUES);
    }

源码解析:

  • 功能:返回该集合的VALUES的枚举(该方法理解不透彻)

9. public synchronized boolean contains(Object value)方法

    public synchronized boolean contains(Object value) {
        if (value == null) {
            throw new NullPointerException();
        }

        Entry<?,?> tab[] = table;
        for (int i = tab.length ; i-- > 0 ;) {
            for (Entry<?,?> e = tab[i] ; e != null ; e = e.next) {
                if (e.value.equals(value)) {
                    return true;
                }
            }
        }
        return false;
    }

源码解析:

  • 功能:判断集合中是否包含值为value的元素
  • 源码思路:
    • (1)首先判断value是否为null,如果为null,则抛出空指针异常。因为hashtable集合中不允许存储null
    • (2)定义一个Entry类型的数组tab,用于存储集合中的元素,实际上table是集合中的桶,即集合中的键
    • (3)两层循环,遍历集合中的元素,外层循环用来遍历集合中的桶,内层循环用来遍历每个桶的链表,通过判断每个元素对应的value值与参数value是否相等来寻找元素

10. public boolean containsValue(Object value)方法

    public boolean containsValue(Object value) {
        return contains(value);
    }

源码解析:

  • 功能:用来判断集合中是否存在value
  • 源码思路:
    • 调用该类的contains()方法来实现该操作

11. public synchronized boolean containsKey(Object key)方法

    public synchronized boolean containsKey(Object key) {
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                return true;
            }
        }
        return false;
    }

源码解析:

  • 功能:判断集合中是否包含键为key的元素
  • 源码思路:
    - 首先定义一个Entry类型的数组,用来存储集合中的元素
    - 定义一个int类型的hash,用来存储想要查找的key的hash码
    - 通过hash码来计算元素在集合中存储的索引值
    - 找到“key对应的Entry(链表)”,然后在链表中找出“哈希值”和“键值”与key对应都相等的元素

12. public synchronized V get(Object key)方法

    public synchronized V get(Object key) {
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                return (V)e.value;
            }
        }
        return null;
    }

源码解析:

  • 功能:通过键key,获取集合中的元素
  • 源码思路:
    • (1)定义一个Entry类型的数组,用来存储集合中的桶
    • (2)定义一个int类型的变量hash,其值等于key的hashCode码
    • (3)使用hashCode码找到元素对应的索引值
    • (4)使用for循环在桶对应的链表中进行遍历,找到符合条件的元素,如果找到就返回该元素的值,否则返回null

13. protected void rehash()方法

    @SuppressWarnings("unchecked")
    protected void rehash() {
        int oldCapacity = table.length;
        Entry<?,?>[] oldMap = table;

        // overflow-conscious code
        int newCapacity = (oldCapacity << 1) + 1;
        if (newCapacity - MAX_ARRAY_SIZE > 0) {
            if (oldCapacity == MAX_ARRAY_SIZE)
                // Keep running with MAX_ARRAY_SIZE buckets
                return;
            newCapacity = MAX_ARRAY_SIZE;
        }
        Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];

        modCount++;
        threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
        table = newMap;

        for (int i = oldCapacity ; i-- > 0 ;) {
            for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
                Entry<K,V> e = old;
                old = old.next;

                int index = (e.hash & 0x7FFFFFFF) % newCapacity;
                e.next = (Entry<K,V>)newMap[index];
                newMap[index] = e;
            }
        }
    }

源码解析:

  • 功能:用来扩容,如果集合中的元素个数超过设置的阈值,则需要进行扩容操作,扩充的容量变为:原容量*2+1,与hashmap有明显区别
  • 源码思路:
    • (1)定义一个int类型的变量,用来存储集合中键的个数
    • (2)定义一个Entry类型的数组oldMap,用来存储集合中桶的信息
    • (3)定义一个int类型的变量newCapacity,其值等集合中原桶的容量*2+1,扩容
    • (4)如果扩充的容量大于集合中设置的最大数组容量,再判断如果旧的容量等于集合中设置的最大数组容量,则不再更改容量,即不扩充容量而直接返回,不进行下面的操作
    • (5)如果旧的容量小于集合中设置的最大数组容量,则将扩充的容量置于集合中设置的最大数组容量
    • (6)定义一个Entry类型的数组newMap,创建一个容量个新扩充容量的桶数组
    • (7)修改modCount的值(因为扩容是对集合的结构进行修改)
    • (8)因为集合的结构修改了,所以要修改集合的阈值
    • (9)将新的容量集合赋值给当前集合
    • (10)通过两层for循环,将原集合中的元素传递到新集合中

例子:
比如初始值11、加载因子默认0.75,那么这个时候阀值threshold=8,当容器中的元素达到8时,HashTable进行一次扩容操作,容量 = 8 * 2 + 1 =17,阀值threshold=17*0.75 = 13,当容器元素再一次达到阀值时,HashTable还会进行扩容操作,容量 = 13 * 2 + 1 = 27 ,阈值 threshold = 27 * 0.75 依次类推。

14. private void addEntry(int hash, K key, V value, int index)方法

    private void addEntry(int hash, K key, V value, int index) {
        modCount++;

        Entry<?,?> tab[] = table;
        if (count >= threshold) {
            // Rehash the table if the threshold is exceeded
            rehash();

            tab = table;
            hash = key.hashCode();
            index = (hash & 0x7FFFFFFF) % tab.length;
        }

        // Creates the new entry.
        @SuppressWarnings("unchecked")
        Entry<K,V> e = (Entry<K,V>) tab[index];
        tab[index] = new Entry<>(hash, key, value, e);
        count++;
    }

源码解析:

  • 功能:向集合中添加键值对
  • 源码思路:
    • (1)首先修改集合的modCount,将其值+1
    • (2)定义一个Entry类型的数组tab,将当前集合的元素赋值给该数组
    • (3)如果集合中的元素大于等于阈值,需要对集合进行扩容,然后再重新将当前集合的元素赋值给新定义的数组tab
    • (4)将想要添加的元素的key的hashCode码存储在参数hash中,通过该hash值来计算该元素所属的索引值
    • (5)将想要存储的元素的信息放在新定义的变量e中,再将变量e的信息放在Entry类型的对象中,将该对象存储在桶对应的索引为止
    • (6)最后修改集合中元素的个数

15. public synchronized V put(K key, V value)方法

    public synchronized V put(K key, V value) {
        // Make sure the value is not null
        if (value == null) {
            throw new NullPointerException();
        }

        // Makes sure the key is not already in the hashtable.
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> entry = (Entry<K,V>)tab[index];
        for(; entry != null ; entry = entry.next) {
            if ((entry.hash == hash) && entry.key.equals(key)) {
                V old = entry.value;
                entry.value = value;
                return old;
            }
        }

        addEntry(hash, key, value, index);
        return null;
    }

源码解析:

  • 功能:向集合中放入key-value,且key和value都不能为null
  • 源码思路:
    • (1)如果value的值为null,则抛出空指针异常
    • (2)定义一个Entry类型的数组tab,将当前集合的元素赋值给该数组
    • (3)定义一个int类型的变量hash,其值等于key的hashCode码
    • (4)使用hashCode码找到元素对应的索引值
    • (5)定义一个Entry类型的变量entry,用来存储集合中该索引值下的桶信息
    • (6)通过for循环进行遍历,直到遍历到链表尾,或者找到相同的hash值为止
    • (7)如果找到想要修改的key值,需要将修改的值value赋值给对应的key的value
    • (8)如果没有找到想要修改的key值,则调用addEntry方法将该键值添加的集合中

例子:
首先我们假设一个容量为5的table,存在8、10、13、16、17、21。他们在table中位置如下:
Java集合(18)——HashTable源码解析

然后我们插入一个数:put(16,22),key=16在table的索引位置为1,同时在1索引位置有两个数,程序对该“链表”进行迭代,发现存在一个key=16,这时要做的工作就是用newValue=22替换oldValue16,并将oldValue=16返回。

Java集合(18)——HashTable源码解析

在put(33,33),key=33所在的索引位置为3,并且在该链表中也没有存在某个key=33的节点,所以就将该节点插入该链表的第一个位置。

Java集合(18)——HashTable源码解析

例子来自:https://blog.csdn.net/shennongzhaizhu/article/details/51871014

16. public synchronized V remove(Object key)方法

    public synchronized V remove(Object key) {
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> e = (Entry<K,V>)tab[index];
        for(Entry<K,V> prev = null ; e != null ; prev = e, e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                modCount++;
                if (prev != null) {
                    prev.next = e.next;
                } else {
                    tab[index] = e.next;
                }
                count--;
                V oldValue = e.value;
                e.value = null;
                return oldValue;
            }
        }
        return null;
    }

源码解析:

  • 功能:从集合中移除键为key的元素
  • 源码思路:
    • (1)定义一个Entry类型的数组tab,其值等于集合中的元素
    • (2)定义一个int类型的变量hash,其值等于key的hashCode码
    • (3)定义一个int类型的变量index,其值等于key对应的集合中桶的索引
    • (4)定义一个Entry类型的变量e,其值等于集合中的桶的位置
    • (5)通过for循环遍历桶中的链表
    • (6)当找到链表中元素的hash码和key与希望移除的元素一致时,将modCount的值+1
    • (7)如果移除的元素不是链表中的第一个元素,则需要将移除的元素的前后用next域连接在一起,防止断链
    • (8)如果移除的元素是链表中的第一个元素,直接将桶的下面的第一个元素设置为移除的下一个元素
    • (9)修改count集合中元素的个数
    • (10)将旧值放在新定义的变量oldValue中,用于返回,将旧值置为null

17. public synchronized void putAll(Map<? extends K, ? extends V> t)方法

    public synchronized void putAll(Map<? extends K, ? extends V> t) {
        for (Map.Entry<? extends K, ? extends V> e : t.entrySet())
            put(e.getKey(), e.getValue());
    }

源码解析:

  • 功能:将集合t中的元素全部放在当前集合中
  • 源码思路:
    • 在该方法中实现该操作只是通过for循环,在循环体中调用put方法实现

18. public synchronized void clear()方法

    public synchronized void clear() {
        Entry<?,?> tab[] = table;
        modCount++;
        for (int index = tab.length; --index >= 0; )
            tab[index] = null;
        count = 0;
    }

源码解析:

  • 功能:将集合中的元素全部清除
  • 源码思路:
    • (1)因为该操作是对集合的结构上进行一次修改,因此需要更改modCount的值
    • (2)通过for循环,将集合中的每个元素都置为null
    • (3)最后修改集合中元素的个数变量count,将其值置为0

19. public synchronized Object clone()方法

    public synchronized Object clone() {
        try {
            Hashtable<?,?> t = (Hashtable<?,?>)super.clone();
            t.table = new Entry<?,?>[table.length];
            for (int i = table.length ; i-- > 0 ; ) {
                t.table[i] = (table[i] != null)
                    ? (Entry<?,?>) table[i].clone() : null;
            }
            t.keySet = null;
            t.entrySet = null;
            t.values = null;
            t.modCount = 0;
            return t;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }

源码解析:

  • 功能:克隆一个hashtable,该方法是浅克隆,只是克隆了hashtable的结构,键值对没有进行深克隆

20. public synchronized String toString()方法

    public synchronized String toString() {
        int max = size() - 1;
        if (max == -1)
            return "{}";

        StringBuilder sb = new StringBuilder();
        Iterator<Map.Entry<K,V>> it = entrySet().iterator();

        sb.append('{');
        for (int i = 0; ; i++) {
            Map.Entry<K,V> e = it.next();
            K key = e.getKey();
            V value = e.getValue();
            sb.append(key   == this ? "(this Map)" : key.toString());
            sb.append('=');
            sb.append(value == this ? "(this Map)" : value.toString());

            if (i == max)
                return sb.append('}').toString();
            sb.append(", ");
        }
    }

21. private <T> Enumeration<T> getEnumeration(int type)方法

    private <T> Enumeration<T> getEnumeration(int type) {
        if (count == 0) {
            return Collections.emptyEnumeration();
        } else {
            return new Enumerator<>(type, false);
        }
    }

源码解析:

  • 功能:获取hashtable的枚举类型对象
  • 源码思路:
    • 如果Hashtable的实际大小为0,则返回“空枚举类”对象;
    • 否则返回征程的Enumerator类型的对象

22. private <T> Iterator<T> getIterator(int type)方法

    private <T> Iterator<T> getIterator(int type) {
        if (count == 0) {
            return Collections.emptyIterator();
        } else {
            return new Enumerator<>(type, true);
        }
    }

源码解析:

  • 功能:获取Hashtable的迭代器
  • 源码思路:
    • 若Hashtable的实际大小为0,则返回“空迭代器”对象;
    • 否则,返回正常的Enumerator的对象。(Enumerator实现了迭代器和枚举两个接口)

23. public Set<K> keySet()方法

    private transient volatile Set<K> keySet;
    private transient volatile Set<Map.Entry<K,V>> entrySet;
    private transient volatile Collection<V> values;
    public Set<K> keySet() {
        if (keySet == null)
            keySet = Collections.synchronizedSet(new KeySet(), this);
        return keySet;
    }

源码解析:

  • 功能:获取集合中的所有键key,返回的类型为Set类型的,因为Set类型的元素不允许重复正好同集合的键不能重复的特性一致
  • 源码思路:
    • 首先判断keySet是否为null,如果为空则调用Collections类的synchronizedSet方法。该方法的作用是:当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块
    • 如果keySet不为null,则直接返回keySet

24. public Set<Map.Entry<K,V>> entrySet()方法

    public Set<Map.Entry<K,V>> entrySet() {
        if (entrySet==null)
            entrySet = Collections.synchronizedSet(new EntrySet(), this);
        return entrySet;
    }

源码解析:

  • 功能:获得集合中的entry实体

25. public Collection<V> values()方法

    public Collection<V> values() {
        if (values==null)
            values = Collections.synchronizedCollection(new ValueCollection(), this);
        return values;
    }

源码解析:

  • 功能:获得集合中的所有值value,返回的类型为Collection类型,因此允许值可以有重复

26. public synchronized boolean equals(Object o)方法

    public synchronized boolean equals(Object o) {
        if (o == this)
            return true;

        if (!(o instanceof Map))
            return false;
        Map<?,?> t = (Map<?,?>) o;
        if (t.size() != size())
            return false;

        try {
            Iterator<Map.Entry<K,V>> i = entrySet().iterator();
            while (i.hasNext()) {
                Map.Entry<K,V> e = i.next();
                K key = e.getKey();
                V value = e.getValue();
                if (value == null) {
                    if (!(t.get(key)==null && t.containsKey(key)))
                        return false;
                } else {
                    if (!value.equals(t.get(key)))
                        return false;
                }
            }
        } catch (ClassCastException unused)   {
            return false;
        } catch (NullPointerException unused) {
            return false;
        }

        return true;
    }

源码解析:

  • 功能:集合的重写的equals方法
  • 源码思路:
    • (1)首先判断当前集合对象与相比较的对象o是否同属一个地址,如果是则说明两个对象相同
    • (2)其次判断对象o是否属于Map集合,如果不属于Map集合,则返回false,否则执行以下操作
    • (3)定义一个Map集合对象t,将相比较对象o强制转换为Map类型
    • (4)判断当前集合中元素的个数与相比较集合中元素的个数是否相等,如果不相等则返回false
    • (5)定义一个迭代器,其值等于entrySet实体的迭代器
    • (6)通过while循环来判断当前集合对象中的元素与相比较的对象中的元素是否相等

27. public synchronized int hashCode()方法

    public synchronized int hashCode() {
        int h = 0;
        if (count == 0 || loadFactor < 0)
            return h;  // Returns zero

        loadFactor = -loadFactor;  // Mark hashCode computation in progress
        Entry<?,?>[] tab = table;
        for (Entry<?,?> entry : tab) {
            while (entry != null) {
                h += entry.hashCode();
                entry = entry.next;
            }
        }

        loadFactor = -loadFactor;  // Mark hashCode computation complete

        return h;
    }

28. public synchronized V getOrDefault(Object key, V defaultValue)方法

    public synchronized V getOrDefault(Object key, V defaultValue) {
        V result = get(key);
        return (null == result) ? defaultValue : result;
    }

源码解析:

  • 功能:找到键为key的值,如果没有键为key的值,则返回默认的value值
  • 源码思路:
    • (1)通过调用get方法获得特定键key的值,得到的值放在新定义的变量result中
    • (2)判断如果result为null,则说明没有想要搜索的键,则返回默认的value,否则返回找到的result

Hashtable计算hash值,直接用key的hashCode(),而HashMap重新计算了key的hash值,Hashtable在求hash值对应的位置索引时,用取模运算,而HashMap在求位置索引时,则用与运算,且这里一般先用hash&0x7FFFFFFF后,再对length取模,&0x7FFFFFFF的目的是为了将负的hash值转化为正值,因为hash值有可能为负数,而&0x7FFFFFFF后,只有符号外改变,而后面的位都不变。