jdk的hashmap,当然比较完美,这里写一个简单的hashmap。
第一版本:
1、hash算法没有hashmap好。
2、数组长度没有做到2的n次方。
3、没有jdk8的红黑树。
4、扩容算法,直接粗暴的重新放到新数组,做了一些无用功,比如,扩容放元素的时候,是不需要比较的,可以直接放置在链表的头部,不需要一个一个遍历比较,然后放到尾部。
4、等等。
public class DiyHashMap {
private DiyHashMap.Node[] nodes;
private int size;
private Integer threshold;
private Float rate;
private Integer length;
public DiyHashMap(Integer length, Float rate) {
this.length = length;
this.rate = rate;
Float temp = length * rate;
this.threshold = temp.intValue();
}
private class Node {
private K key;
private V value;
private Node next;
public Node(K key, V value) {
this.key = key;
this.value = value;
}
}
public void put(K k, V v) {
if (nodes == null) {
nodes = new DiyHashMap.Node[length];
}
if (size >= threshold) {
// 扩容
resize();
}
Node node = new Node(k, v);
int index = indexOfArray(k.hashCode());
if (nodes[index] == null) {
nodes[index] = node;
size++;
} else {
// 链表查找
Node n = nodes[index];
while (true) {
if (n.next == null) {
// 直接队列尾放置
n.next = node;
size++;
break;
} else if (n.next.key.hashCode() == k.hashCode() && n.next.key.equals(k)) {
// 替换
n.next.value = v;
break;
} else {
// 往下遍历
n = n.next;
}
}
}
}
public void resize() {
int newLength = length << 1;
this.length = newLength;
Float temp = length * rate;
this.threshold = temp.intValue();
this.size = 0;
Node[] oldNodes = nodes;
DiyHashMap.Node[] newNodes = new DiyHashMap.Node[newLength];
this.nodes = newNodes;
for (int i = 0; i < oldNodes.length; i++) {
Node oldNode = oldNodes[i];
while (true) {
if (oldNode != null) {
this.put(oldNode.key, oldNode.value);
oldNode = oldNode.next;
} else {
break;
}
}
}
}
public int indexOfArray(int hashcode) {
int i = hashcode % length;
if(i < 0){
return 0 - i;
}
return i;
}
public V get(K k) {
int index = indexOfArray(k.hashCode());
Node node = nodes[index];
while (true) {
if (node == null) {
return null;
} else if (node.key.hashCode() == k.hashCode() && node.key.equals(k)) {
// 找到了
return node.value;
} else {
node = node.next;
}
}
}
}
测试:正确
public static void main(String[] args) {
DiyHashMap map = new DiyHashMap<>(2, 0.75f);
String key = "hello";
for(int i = 0; i < 10000; i++){
map.put(key + i, "world" + i);
}
for(int i = 0; i < 10000; i++){
String value = map.get(key + i);
if(!value.equals("world" + i)){
System.out.println("fail");
}
}
}
再来一个版本:
1、优化了一下扩容。
2、因为2的n次方,需要比较麻烦的位运算,所以除非复制hashmap的代码,否则,手写不出来。。
2的n次方写不出,那么取模运算,也就优化不了。
2的n次方写不出来,就不能用高位,去推算扩容后的位置,所以扩容后也就只能链表倒置了。
3、红黑树太复杂了,也不写。
public class DiyHashMapV2 {
private DiyHashMapV2.Node[] nodes;
private int size;
private Integer threshold;
private Float rate;
private Integer length;
public DiyHashMapV2(Integer length, Float rate) {
this.length = length;
this.rate = rate;
Float temp = length * rate;
this.threshold = temp.intValue();
}
private class Node {
private K key;
private V value;
private Node next;
public Node(K key, V value) {
this.key = key;
this.value = value;
}
}
public void put(K k, V v) {
if (nodes == null) {
nodes = new DiyHashMapV2.Node[length];
}
if (size >= threshold) {
// 扩容
resize();
}
Node node = new Node(k, v);
int index = indexOfArray(k.hashCode());
if (nodes[index] == null) {
nodes[index] = node;
size++;
} else {
// 链表查找
Node n = nodes[index];
while (true) {
if (n.next == null) {
// 直接队列尾放置
n.next = node;
size++;
break;
} else if (n.next.key.hashCode() == k.hashCode() && n.next.key.equals(k)) {
// 替换
n.next.value = v;
break;
} else {
// 往下遍历
n = n.next;
}
}
}
}
// 就重写了一下扩容方法
public void resize() {
int newLength = length << 1;
this.length = newLength;
Float temp = length * rate;
this.threshold = temp.intValue();
Node[] oldNodes = nodes;
DiyHashMapV2.Node[] newNodes = new DiyHashMapV2.Node[newLength];
this.nodes = newNodes;
for (int i = 0; i < oldNodes.length; i++) {
Node oldNode = oldNodes[i];
while (true) {
if (oldNode != null) {
int newIndex = indexOfArray(oldNode.key.hashCode());
// 新数组的这个位置是否有值
if (null == newNodes[newIndex]) {
newNodes[newIndex] = oldNode;
oldNode = oldNode.next;
// 这里需要把以前的next指标清空,否则,因为,扩容后,链表倒置了,会造成链表循环next,也就是链表的最后一个元素的next会指向链表的第一个元素
newNodes[newIndex].next = null;
} else {
// 直接放到头部
Node tmp = oldNode.next;
Node t = newNodes[newIndex];
newNodes[newIndex] = oldNode;
oldNode.next = t;
oldNode = tmp;
}
} else {
break;
}
}
}
}
public int indexOfArray(int hashcode) {
int i = hashcode % length;
if(i < 0){
return 0 - i;
}
return i;
// return 1;
}
public V get(K k) {
int index = indexOfArray(k.hashCode());
Node node = nodes[index];
while (true) {
if (node == null) {
return null;
} else if (node.key.hashCode() == k.hashCode() && node.key.equals(k)) {
// 找到了
return node.value;
} else {
node = node.next;
}
}
}
}
测试一下性能:
测试结果其实性能差不多。
测试的结果不是很精确,因为每次都不一样,跟,cpu的波动,分配的执行时间有关。
public static void main(String[] args) {
String key = "hello";
DiyHashMap map = new DiyHashMap<>(16, 0.75f);
for (int i = 0; i < 5000000; i++) {
map.put(key + i, "world" + i);
}
long start = System.nanoTime();
DiyHashMap map1 = new DiyHashMap<>(16, 0.75f);
for (int i = 0; i < 5000000; i++) {
map1.put(key + i, "world" + i);
}
/*
测试,能不能取到元素
for (int i = 0; i < 10000; i++) {
String value = map.get(key + i);
if (!value.equals("world" + i)) {
System.out.println("fail");
}
}*/
long end = System.nanoTime();
DiyHashMapV2 map2 = new DiyHashMapV2<>(16, 0.75f);
for (int i = 0; i < 5000000; i++) {
map2.put(key + i, "world" + i);
}
long end2 = System.nanoTime();
Map map3 = new HashMap<>(16, 0.75f);
for (int i = 0; i < 5000000; i++) {
map3.put(key + i, "world" + i);
}
long end3 = System.nanoTime();
System.out.println(end - start);
System.out.println(end2 - end);
System.out.println(end3 - end2);
}
3128620097
5787549157
3656479197