HashSet、HashMap

来源:互联网 发布:js split 数组 length 编辑:程序博客网 时间:2024/06/07 01:05

HashSet添加元素方法的本质也是调用HashMap
这里写图片描述

HashMap实现原理:
移位、^运算=hash值
能否添加进HashMap集合当中呢?
3个依据:判断hash值、是否为同一对象、equals方法是否相等
e.hash == hash && ((k = e.key) == key || key.equals(k))

Entry<K,V> e = table[i]; e != null; e = e.next//取桶里的对象,如果桶里没有对象即立刻添加进来,如果有需要,可以进行桶的扩容
void addEntry(int hash, K key, V value, int bucketIndex) {        if ((size >= threshold) && (null != table[bucketIndex])) {            resize(2 * table.length);            hash = (null != key) ? hash(key) : 0;            bucketIndex = indexFor(hash, table.length);        }        createEntry(hash, key, value, bucketIndex);    }  void createEntry(int hash, K key, V value, int bucketIndex) {        Entry<K,V> e = table[bucketIndex];        table[bucketIndex] = new Entry<>(hash, key, value, e);        size++;    }
Entry<K,V> e = table[i]取桶里的对象,如果桶里没有对象即立刻添加进来,如果有对象则判断桶里的hash值是否等于要放进桶里的对象的hash值,如果相等,再判断是否为同一个对象。Entry<K,V> e = table[i]取桶里的对象,判断桶里的hash值和将要放进桶里的对象的hash值是否相等,如相等,再判断是否为同一个对象,根据(k = e.key) == keykey值来做判断,如果为同一个对象,说明桶里已经存在该key则为同一个对象, V oldValue = e.value即用药放进桶里的value值取代原来桶里的value值。Entry<K,V> e = table[i]即用来取桶里的元素,有可能存在,也有可能不存在,如果hash值一样即e.hash == hash为true,但是(k = e.key) == keyfalse,说明不是同一个对象。再判断key.equals(k)即判断该对象的equals方法是否相等。如果相等, V oldValue = e.value即用药放进桶里的value值取代原来桶里的value值。
    public V put(K key, V value) {        if (key == null)            return putForNullKey(value);        int hash = hash(key);  //计算新hash        int i = indexFor(hash, table.length);        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))) {                V oldValue = e.value;                e.value = value;                e.recordAccess(this);                return oldValue;            }        }        modCount++;        addEntry(hash, key, value, i);        return null;    }

int hash(key)=原hash移位得到新hash并和原hash进行^异或运算

  final int hash(Object k) {        int h = 0;        if (useAltHashing) {            if (k instanceof String) {                return sun.misc.Hashing.stringHash32((String) k);            }            h = hashSeed;        }        h ^= k.hashCode();        // 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);    }

e = e.next创建新节点

 static class Entry<K,V> implements Map.Entry<K,V> {        final K key;        V value;        Entry<K,V> next;        int hash;        /**         * Creates new entry.         */        Entry(int h, K k, V v, Entry<K,V> n) {            value = v;            next = n;            key = k;            hash = h;        }

===================================

判断HashSet集合是否已经存在某个对象
代码:

package com.zbiti.java;import java.util.HashSet;import java.util.Set;import org.junit.Test;public class SetDemo {    public static void main(String[] args) {    }    @Test    public void test1(){        Set<Dog> dogs = new HashSet<Dog>();        dogs.add(new Dog("white"));        dogs.add(new Dog("white"));        System.out.println(dogs.contains(new Dog("white")));    //fasle    }}class Dog{    private String name;    public Dog(String name) {        this.name = name;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }}

结果:输出false
原因:HashSet的contains方法的实现如下:

 public boolean contains(Object o) {        return map.containsKey(o);    }
 public boolean containsKey(Object key) {        return getEntry(key) != null;    }
 final Entry<K,V> getEntry(Object key) {        int hash = (key == null) ? 0 : hash(key);        for (Entry<K,V> e = table[indexFor(hash, table.length)];             e != null;             e = e.next) {            Object k;            if (e.hash == hash &&                ((k = e.key) == key || (key != null && key.equals(k))))                return e;        }        return null;    }

解读:

int hash = (key == null) ? 0 : hash(key);先判断添加方法里是否有对象,如果有对象计算出新hash值, for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null;e = e.next) 先判断桶里是否有对象,如果有对象取出对象 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))   return e;    return null;   判断hash值是否相等,即放进桶里对象和已经存在桶里对象的hash值是否相等。如不想等,返回null,说明桶里不存在该对象。   如相等,再判断对象是否为同一个对象。   如果是同一对象,返回该对象。   如果不是同一个对象,再判断对象的equals是否相等,即桶里的对象和将要放进桶里的对象的equals方法的比较。

代码:

package com.zbiti.java;import java.util.HashSet;import java.util.Set;import org.junit.Test;public class SetDemo {    public static void main(String[] args) {    }    @Test    public void test1(){        Set<Dog> dogs = new HashSet<Dog>();        dogs.add(new Dog("white"));        dogs.add(new Dog("white"));        System.out.println(dogs.contains(new Dog("white")));  //false    }}class Dog{    private String name;    public Dog(String name) {        this.name = name;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    @Override    public int hashCode() {        return 1;    }}

结果:输出false
分析:Dog类的hashCode方法被我们重写了,根据上面的分析,桶里2只dog和contains里的dog的hash值是一样的都为1,。所以需要再判断是否 为同一个对象,明显通过new 产生了新的对象,所以并不是同一个对象。直接返回了null。

代码:

package com.zbiti.java;import java.util.HashSet;import java.util.Set;import org.junit.Test;public class SetDemo {    public static void main(String[] args) {    }    @Test    public void test1(){        Set<Dog> dogs = new HashSet<Dog>();        Dog d1 = new Dog("white");        Dog d2 = new Dog("white");        dogs.add(d1);        dogs.add(d2);        System.out.println(dogs.contains(new Dog("white")));  //true    }}class Dog{    private String name;    public Dog(String name) {        this.name = name;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    @Override    public int hashCode() {        return 1;    }    @Override    public boolean equals(Object obj) {        return true;    }}

结果:输出true
分析:桶里含有2个Dog对象,HashSet调用contains方法传进了一个新的Dog对象,由于Dog类里的hashcode、equals方法都被我们重写了,所以不管创建多少个对象,它们调用hashcode、equals方法的返回值都是一样的。根据HashMap的实现原理判断的3个原则,hash值相等,尽管对象不一样,但是equals返回值相等即说明桶里存在该对象。

代码:

package com.zbiti.java;import java.util.HashSet;import java.util.Set;import org.junit.Test;public class SetDemo {    public static void main(String[] args) {    }    @Test    public void test1(){        Set<Dog> dogs = new HashSet<Dog>();        Dog d1 = new Dog("white");        Dog d2 = new Dog("white");        dogs.add(d1);        dogs.add(d2);        System.out.println(dogs.contains(new Dog("white")));  //false    }}class Dog{    private String name;    public Dog(String name) {        this.name = name;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    @Override    public int hashCode() {        return 1;    }    @Override    public boolean equals(Object obj) {        return false;    }}

结果:输出false
分析:Dog类hashcode()、equals()被我们重写,调用hashcode返回的值是相等的,由于对象不是同一个对象并且调用equals方法返回false,所以输出的结果为false。

代码:

package com.zbiti.java;import java.util.HashSet;import java.util.Set;import org.junit.Test;public class SetDemo {    public static void main(String[] args) {    }    @Test    public void test1(){        Set<Dog> dogs = new HashSet<Dog>();        Dog d1 = new Dog("white");        Dog d2 = new Dog("white");        dogs.add(d1);        dogs.add(d2);        System.out.println(dogs.contains(d1));    }   //true}class Dog{    private String name;    public Dog(String name) {        this.name = name;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    @Override    public int hashCode() {        return 1;    }    @Override    public boolean equals(Object obj) {        return false;    }}

结果:输出true
分析:Dog类hashcode()、equals()被我们重写,调用hashcode返回的值是相等的,由于桶里的对象存在和将要放进桶里的对象是同一个对象,尽管调用equals方法返回false,结果的输出依然为true。