知道hashmap的key是自定义类需要重写hashCode和equals,是从面经知道的,当时也是只是去背而已,也尝试着去理解,但是没有实操做事很难受而且会有些问题。
最近写的一个项目时候基于Netty的节点注册事件系统,从中有一个需要自定义map的key值的环节,定义如下:
package NettyEventTouch.Nodes;
public final class Node{
private final String node;
private final String data;
public Node(String node,String data){
this.node = node;
this.data = data;
}
public String getNode() {
return node;
}
public String getData() {
return data;
}
/*
只是用node的原因是防止数据的改变而导致整个node的不可用
*/
@Override
public int hashCode() {
return node.length()^(node.length()/2);
}
//按照面经的做法,一开始的自以为
public boolean equals(Node node1) {
return this.getNode().equals(node1.getNode());
}
// @Override
// public boolean equals(Object obj) {
// return this.getNode().equals(((Node)obj).getNode());
// }
@Override
public String toString() {
return "Node{" +
"node='" + node + '\'' +
", data='" + data + '\'' +
'}';
}
}
简单的测试:
package NettyEventTouch;
import NettyEventTouch.Nodes.Node;
import java.util.HashMap;
public class test {
public static void main(String[] args) {
Node node = new Node("123","123456789");
HashMap<Node,String> map = new HashMap<>();
map.put(node,"123");
Node node2 = new Node(node.getNode(),"11111111");
if (map.containsKey(node2)){
String s = map.get(node2);
System.out.println(s);
}
}
}
结果可想而知并没有进入if语句,所以失败,但是为什么是被需要追寻的:通过debug追到map中的这样一段代码:
/**
* Implements Map.get and related methods.
*
* @param hash hash for key
* @param key the key
* @return the node, or null if none
*/
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
//开始判断
/**
1.先看看hash是不是一样的,如果一样的,那么继续,上面的代码中只是简单的使用了node.length()^(node.length()/2)、
来作为hash值,由于在test中node的node值没有改变所以hash是不变的,所以这个判断视为真
2.来看后半段的逻辑,如果map的key是一样的,那么为真,但是这个是不可能的,因为node2是新创建出来的,对于非基本类型那么==比较的就是累的地址信息,所以显然两个对象不是一个地址,所以这个判断为false,后面的就是比较key的值了,先判断key是否为空,显然不为空,所以判断key.equals(k),这个但是Node里的equals并没有继承Object的equals,因为Node中的equals的参数类型变了,所以这里不会被引用到素以这里调用的是Object的equals,其实就是==,那么理所当然是false的。
*/
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
/**
处理key冲突时的数据的获取
1.先判断是不是红黑树节点,如果是那么去红黑树里取值
2.如果不是的话那么就挨个的取得下一个节点并判断其是否符合要求
*/
if ((e = first.next) != null) {
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
从上面的可以看出来了,由于我的equals没有实现Object的equals,所以在内部调用的时候没有调用到,所以需要改下代码,也即被我注释掉的那个:
@Override
public boolean equals(Object obj) {
return this.getNode().equals(((Node)obj).getNode());
}
这个就可以了,所以这里可以验证了面经的正确,如果hashmap的key是自定义类那么就需要重写hashCode和equals,不然会出现数据是一样的,但是hashmap会报没有key值的问题。
结果: