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) == key的key值来做判断,如果为同一个对象,说明桶里已经存在该key则为同一个对象, V oldValue = e.value即用药放进桶里的value值取代原来桶里的value值。
Entry<K,V> e = table[i]即用来取桶里的元素,有可能存在,也有可能不存在,如果hash值一样即e.hash == hash为true,但是(k = e.key) == key 为false,说明不是同一个对象。再判断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。