对 HashMap 的一次扩展
一、需求
(1) 容量200 Capacity = 200. ——> 静态数据,可提供设置.
(2) 最近操作(get/put)的元数,总放在最前. ——> 元素频繁位移,从效率考虑,用链表实现.
(3) 超过 Capacity 值后,移除最后一个元素,即第200号元素. ——> 结合下一条,满载后,可能频繁操作末端元素,考虑用双向链表.
二、数据结构设计
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 @RequiredArgsConstructor 2 class ButterHashMap<K,V> extends LinkedHashMap<K,V>{ 3 @NonNull 4 @Getter@Setter 5 int capacity; // 容量值 6 @Getter@Setter 7 int limit; // 当前已用量 8 DoubleLinkedNode table; 9 10 @RequiredArgsConstructor 11 class DoubleLinkedNode<K,V> extends LinkedHashMap { 12 @NonNull 13 @Getter 14 @Setter 15 K k; 16 @NonNull 17 @Getter 18 @Setter 19 V v; 20 DoubleLinkedNode head; 21 DoubleLinkedNode pre; 22 @Getter@Setter 23 DoubleLinkedNode next; 24 DoubleLinkedNode tail; 25 } 26 27 }
三、功能实现
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 @RequiredArgsConstructor 2 class ButterHashMap<K,V> extends LinkedHashMap<K,V>{ 3 @Override 4 public V put(K key,V value){ 5 if(checkCapacity()){ 6 // 容量未满:添加首节点 7 this.createNode(key, value, false); 8 }else{ 9 // 容量已满:添加首节点,移除尾节点 10 this.createNode(key, value, true); 11 } 12 System.out.println("添加后——limit : "+ limit + ", capacity : "+ capacity); 13 return value; 14 } 15 16 private boolean checkCapacity(){ 17 int flag = limit+1; 18 if(flag > capacity) return false; 19 return true; 20 } 21 22 // 不能判断 是否存在 K-V,因为 V 可能正好为 null; 23 public V getValue(K key){ 24 DoubleLinkedNode<K,V> e = getNode(key); 25 if(e != null) return e.v; 26 return null; 27 } 28 29 private DoubleLinkedNode createNode(K key,V value,boolean isFlow){ 30 // 先查询一下 31 DoubleLinkedNode src = this.getNode(key); 32 if(src != null){ 33 // 有值,替换 value 即可 34 src.setV(value); 35 return src; 36 } 37 38 DoubleLinkedNode newNode = new DoubleLinkedNode(key, value); 39 if(!isFlow && limit == 0){ 40 this.table = newNode.head = newNode.tail = newNode; 41 limit++; 42 return this.table; 43 } 44 45 this.table.pre = newNode; 46 newNode.next = this.table; 47 newNode.tail = this.table.tail; 48 this.table = newNode; 49 this.table.head = newNode; 50 if(isFlow){ 51 // 容量已满:尾节点,向前移一个节点,节点总数不变 52 this.table.tail = this.table.tail.pre; 53 // 相当于移除原来的尾节点 54 this.table.tail.next = null; 55 }else{ 56 limit++; 57 } 58 return this.table; 59 } 60 61 // 提供 public 的原因在于,本方法 能判断 是否存在 K-V. 若存在,返回值不为 null. 62 public DoubleLinkedNode<K,V> getNode(K key){ 63 if (this.table == null) return null; 64 65 DoubleLinkedNode<K,V> t = this.table,c = this.table, 66 stop = this.table.head,h = this.table.head; 67 // t:table, c:change, h:head 68 while (c.getK() != key){ 69 c = c.next; 70 if(c == null || stop == c){ 71 // 未找到节点 72 return null; 73 } 74 } 75 76 // A : getA 77 // tail、head 无需调整 78 if(h.next == null){ 79 return h; 80 } 81 82 // A ——> B : getA : B ——> A 83 // tail 有影响,需要调整;head 需要调整 84 if(c.next == null){ 85 c.pre.next = null; 86 c.pre.tail = c.pre; 87 c.tail = c.pre; 88 89 c.next = t.head; 90 t.head = c; 91 t.pre = c; 92 c.next = t; 93 t.tail = c.pre; 94 95 c.head = c; 96 c.tail = c.pre; 97 c.pre = null; 98 this.table = c; 99 this.table.head = c; 100 this.table.next = h; 101 this.table.tail = c.tail; 102 return c; 103 } 104 105 // A ——> B ——> C:getB:A ——> C ——> B 106 // tail 无影响,无需调整;head 需要调整 107 c.next.pre = c.pre; 108 c.pre.next = c.next; 109 c.next.head = c; 110 c.pre.head = c; 111 112 h.head = c; 113 h.pre = c; 114 c.next = t; 115 c.head = c; 116 117 c.pre = null; 118 this.table = c; 119 this.table.head = c; 120 this.table.next = h; 121 return c; 122 } 123 }
四、测试检验
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package test; 2 3 import lombok.*; 4 5 import java.util.*; 6 7 8 public class ButterHashMapTest { 9 10 public static void main(String[] args) { 11 12 ButterHashMapTest bt = new ButterHashMapTest(); 13 bt.testButterHashMap(); 14 } 15 16 void testButterHashMap(){ 17 ButterHashMap betterHashMap = new ButterHashMap(5); 18 betterHashMap.put("a",1); 19 betterHashMap.put("b",2); 20 betterHashMap.put("c",3); 21 betterHashMap.put("d",4); 22 this.printMap("put(d-4)", betterHashMap); 23 betterHashMap.put("e",5); 24 this.printMap("put(e-5)", betterHashMap); 25 betterHashMap.put("c",33); 26 System.out.println("========添加相同的 ‘c’:33 《《 ============"); 27 this.printMap("put(e-5)", betterHashMap); 28 System.out.println("========添加相同的 ‘c’:33 》》============"); 29 betterHashMap.put("f",6); 30 this.printMap("put(f-6)", betterHashMap); 31 betterHashMap.getValue("e"); 32 this.printMap("getE", betterHashMap); 33 } 34 35 public void printMap(String context, ButterHashMap betterHashMap){ 36 System.out.println("打印" +context+ "开始《《《==============="); 37 if(betterHashMap != null){ 38 ButterHashMap.DoubleLinkedNode p = betterHashMap.table, 39 s = p; 40 while (s != null){ 41 System.out.println("key:"+s.k + ", value:" + s.v); 42 s = s.next; 43 } 44 45 System.out.println("打印" +context+ "结束》》》==============="); 46 47 } 48 } 49 }
五、测试结果
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 添加后——limit : 1, capacity : 5 2 添加后——limit : 2, capacity : 5 3 添加后——limit : 3, capacity : 5 4 添加后——limit : 4, capacity : 5 5 打印put(d-4)开始《《《=============== 6 key:d, value:4 7 key:c, value:3 8 key:b, value:2 9 key:a, value:1 10 打印put(d-4)结束》》》=============== 11 添加后——limit : 5, capacity : 5 12 打印put(e-5)开始《《《=============== 13 key:e, value:5 14 key:d, value:4 15 key:c, value:3 16 key:b, value:2 17 key:a, value:1 18 打印put(e-5)结束》》》=============== 19 添加后——limit : 5, capacity : 5 20 ========添加相同的 ‘c’:33 《《 ============ 21 打印put(e-5)开始《《《=============== 22 key:c, value:33 23 key:e, value:5 24 key:d, value:4 25 key:b, value:2 26 key:a, value:1 27 打印put(e-5)结束》》》=============== 28 ========添加相同的 ‘c’:33 》》============ 29 添加后——limit : 5, capacity : 5 30 打印put(f-6)开始《《《=============== 31 key:f, value:6 32 key:c, value:33 33 key:e, value:5 34 key:d, value:4 35 key:b, value:2 36 打印put(f-6)结束》》》=============== 37 打印getE开始《《《=============== 38 key:e, value:5 39 key:f, value:6 40 key:c, value:33 41 key:d, value:4 42 key:b, value:2 43 打印getE结束》》》=============== 44 45 Process finished with exit code 0