目录
说hashCode()方法之前先来介绍一下什么是哈希值:
哈希值:是数据存储的逻辑地址,类似于房间的门牌号。
Object的hashCode()方法:
JDK源吗:
public native int hashCode();
跟句源吗知道这仅仅是一个类似于接口形式的方法,子类可以选择去重写该方法;
引用类型的hashCode()方法:
创建User.java:(没有重写hashCode()方法)
public class User {
private String username;
private String password;
public User() {
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
}
创建测试类test.java:
User user1=new User("tom","11");
User user2=new User("tom","11");
System.out.println(user1.hashCode());//1163157884
System.out.println(user2.hashCode());//1956725890
可见两个对象虽然值都相同,但是哈希值是不同的;
基本类型String的hashCode()方法:
JDK源码:
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
根据源码可以看出相同的对象的哈希值是相同的,我对结论进行验证:
String s1="11";
String s2=new String("11");
String s3="11";
System.out.println(s1.hashCode());//1568
System.out.println(s2.hashCode());//1568
System.out.println(s3.hashCode());//1568
根据输出证明了我的猜想是对的。(字符串值相同的元素的哈希值是一样的)
好了,有了前边的例子进行铺垫,我下边对HashSet的存储进行了研究
引用类型的HashSet存储:
在测试类test中添加以下代码:
HashSet<User> hashSet=new HashSet<User>();
hashSet.add(user1);
hashSet.add(user2);
for (User u : hashSet){
System.out.println(u.getUsername()+"*********"+u.getPassword());
}
输出:tom*********11
tom*********11
可见HashSet没有重写Object的hachCode()方法而导致user1和user2的哈希值不同,一起存入哈希表中。
所以想要保证哈希表中元素的唯一性,需要重写hachCode()方法,然而根据add()的JDK源码:
//HashSet
private transient HashMap<E,Object> map;
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
//HashMap
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
如果想要保证元素的唯一性,当创建了相同的元素添加到哈希表中,需要把哈希值设置成相同的,而且还要重写equals()方法;
在User.java中自动重写hachCode()和equals()方法:
@Override
public int hashCode() {
return Objects.hash(username, password);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User)) return false;
User user = (User) o;
return Objects.equals(getUsername(), user.getUsername()) &&
Objects.equals(getPassword(), user.getPassword());
}
又根据 Objects.hash源码发现:
//Objects.java:
public static int hash(Object... values) {
return Arrays.hashCode(values);
}
查看Arrays.hashCode方法:
public static int hashCode(Object a[]) {
if (a == null)
return 0;
int result = 1;
for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());
return result;
}
通过该方法使得值相同的对象拥有了相同的哈希值。又根据equals()判断值是否相等,,保证了HashSet元素的唯一性。
此时,再次运行测试类test.java:
输出:tom*********11
试试证明了开始的猜想是对的。
String基本类型的HashSet存储:
String s1="11";
String s2=new String("11");
String s3="11";
System.out.println(s1.hashCode());//1568
System.out.println(s2.hashCode());//1568
System.out.println(s3.hashCode());//1568
if (s1.equals(s2)){
System.out.println("相等");
}
else {
System.out.println("不相等");
}//相等
//比较地址值
System.out.println(s1==s2);//false
System.out.println(s1==s3);//true
HashSet<String> s=new HashSet<String>();
s.add(s1);
s.add(s2);
s.add(s3);
for (String a : s){
System.out.println(a);
}
//11
由于String类即重写了hashCode()方法和equals()方法,所以保证了添加到哈希表中的元素的唯一性。