HashMap集合中的储存的是偶对象,即键值对应关系:key = value。在调用put()方法添加数据时,保存的顺序并不是添加的顺序。简单来理解就是,首先根据key的hashCode进行一定的运算来实现分类,保存在对应的“桶”中。在数据量较小时,运算所得值相同的对象在同一“桶”中是以链表的形式存在的;当数据增大到一定量(未达到集合扩容条件前),则变为红黑树的形式储存,以提高查询等方面的效率。
同样的,而在使用get()方法进行取出/查询时,首先是根据传入的key的hashCode找到所在的“桶”,再在此桶中使用key的equals()方法进行对比查询。
由上可知,如果要使用自定义类的对象作为key,需要覆写hashCode()和equals()方法,否则它将会调用Object父类的方法,无法满足需要的对比条件。
一:自定义Person类作为key,并覆写hashCode()和equals():
package com.java.demo;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
class Person {
private String name;
public Person() {}
public Person(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() { //覆写hashCode()方法
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) { //覆写equals()方法
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
public String toString() {
return "姓名:"+ this.name;
}
}
二:测试类:
public class TestDemo {
public static void main(String[] args){
Map keyPerson = new HashMap();
Person person = new Person("李四");
Person person2 = person;
keyPerson.put(person, new String("LS"));
keyPerson.put(new Person("一"), new String("1"));
keyPerson.put(new Person("二"), new String("2"));
System.out.println(keyPerson.get(person)); //LS
System.out.println(keyPerson.get(person2)); //LS
System.out.println(keyPerson.get(new Person("李四"))); //LS
}
}
运行结果如预期:
LS
LS
LS
那么如果此时将person对象的name属性修改会如何呢:person.setName("滚滚");?
public class TestDemo {
public static void main(String[] args){
Map keyPerson = new HashMap();
Person person = new Person("李四");
Person person2 = person;
keyPerson.put(person, new String("LS"));
keyPerson.put(new Person("一"), new String("1"));
keyPerson.put(new Person("二"), new String("2"));
person.setName("滚滚");
Iterator> iter = keyPerson.entrySet().iterator();
while(iter.hasNext()) {
Map.Entry entry= iter.next();
Person p = entry.getKey();
String s = entry.getValue();
System.out.println(p+" = "+s);
}
System.out.println();
System.out.println(keyPerson.get(new Person("李四")));//null
System.out.println(keyPerson.get(new Person("滚滚")));//null
System.out.println(keyPerson.get(person));//null
System.out.println(keyPerson.get(person2));//null
}
}
运行结果:
姓名:滚滚 = LS
姓名:二 = 2
姓名:一 = 1
null
null
null
null
迭代查询可以发现,person及其对应的值LS都还在Map集合中,只是person的内容改变了:李四→滚滚。但是后面的get()方法,不论是用之前的new Person("李四"),还是new Person("滚滚")/person对象,均无法找到对应值,返回为null。
为什么呢?
(此时数据较小,暂时只讨论链表存储的内部结构。)
之前对HashMap的储存和查询取出有一定的了解,那么就知道了,在修改之前,根据person:new Person("李四")对象的hashCode,数据储存在一个对应的“桶”中(比如桶2中),在setName后,person的hashCode和内容均发生了变化,改变后的hashCode本应该是对应桶3,但是此时它还是在桶2中。
此后在调用get()方法查找时,“滚滚”是在桶3中查找,桶3中没有对应数据,所以返回null;“李四”是在桶2中查询,但是此时桶二中的key值已变为“滚滚”,equals()方法返回false,即也找不到对应值,get()返回null。
为了印证,我们将“李四”和“滚滚”的hashCode统一为一样的,即保证即使setName()后,key的hashCode对应的仍然是同一个桶,这样在get()查询时,是在同一个桶中查询的:
@Override
public int hashCode() { //覆写hashCode()方法
if(this.name.equals("李四")||this.name.equals("滚滚")) {
return 37;
}
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
get():
System.out.println(keyPerson.get(new Person("李四")));//null
System.out.println(keyPerson.get(new Person("滚滚")));//LS
System.out.println(keyPerson.get(person));//LS
System.out.println(keyPerson.get(person2));//LS
运行结果如预期:
null
LS
LS
LS
完整代码:
package com.java.demo;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
class Person {
private String name;
public Person() {}
public Person(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() { //覆写hashCode()方法
if(this.name.equals("李四")||this.name.equals("滚滚")) {
return 37;
}
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {//覆写equals()方法
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
public String toString() {
return "姓名:"+ this.name;
}
}
public class TestDemo {
public static void main(String[] args){
Map keyPerson = new HashMap();
Person person = new Person("李四");
Person person2 = person;
keyPerson.put(person, new String("LS"));
keyPerson.put(new Person("一"), new String("1"));
keyPerson.put(new Person("二"), new String("2"));
System.out.println(keyPerson.get(person));//LS
System.out.println(keyPerson.get(person2));//LS
System.out.println(keyPerson.get(new Person("李四")));//LS
System.out.println();
person.setName("滚滚");
Iterator> iter = keyPerson.entrySet().iterator();
while(iter.hasNext()) {
Map.Entry entry= iter.next();
Person p = entry.getKey();
String s = entry.getValue();
System.out.println(p+" = "+s);
}
System.out.println();
System.out.println(keyPerson.get(new Person("李四")));//null
System.out.println(keyPerson.get(new Person("滚滚")));//LS
System.out.println(keyPerson.get(person));//LS
System.out.println(keyPerson.get(person2));//LS
}
}