赞
踩
1)存储键值对:
key | value |
---|---|
电脑 | $1600 |
手机 | $12 |
导管 | $1 |
2)查询效率要求高:O(1)级别,快速查询,空间换取时间。
数据结构的两个基本基础单元为:数组和链表,分别以数组和链表为基础,设计存储key-value的数据结构。(键值对:String-Integer,完成put和get方法)
// 数组构建 三个方法:1.get() 2.put() 3.resize() public class KeyValue { private Node[] table; private int size = 0; private int capacity = 0; final private int DEFAULT_INITIAL_CAPACITY= 16; public KeyValue(){ capacity = DEFAULT_INITIAL_CAPACITY; table = new Node[capacity]; } //1.查找元素 public Integer get(String key) { for (int i = 0; i < size; i++) { if (table[i].key.equals(key)) { return table[i].value; } } return null; } //2.添加元素 public Integer put(String key,Integer value){ //查找key是否已存在,存在则替换 for (int i = 0; i < size; i++) { if(table[i].key.equals(key)){ table[i].value = value; return value; } } size ++; if (size > capacity) resize(); table[size] = new Node(key, value); return value; } //3.扩容 private void resize(){ capacity <<= 1; Node[] newTable = new Node[capacity]; for (int i = 0;i < table.length;i++){ newTable[i] = table[i]; } table = newTable; } //存储单元 class Node{ String key; Integer value; public Node(String key, Integer value) { this.key = key; this.value = value; } } }
//链表构建 2个方法:1.get() 2.put() public class KeyValue { private LinkedList<Node> table; private int size = 0; public KeyValue(){ table = new LinkedList<Node>(); } //1.查找元素 public Integer get(String key) { for (int i = 0; i < table.size(); i++) { Node temp = table.get(i); if(temp.key.equals(key)){return temp.value;} } return null; } //2.添加元素 public Integer put(String key,Integer value){ //查找key是否已存在,存在则替换 for (int i = 0; i < table.size(); i++) { Node temp = table.get(i); if(temp.key.equals(key)){ temp.value = value; return value; } } size ++; Node newNode = new Node(key, value); table.add(newNode); return value; } //存储单元 class Node{ String key; Integer value; public Node(String key, Integer value) { this.key = key; this.value = value; } } }
* | 数组 | 链表 |
---|---|---|
查询 | O(n) | O(n) |
添加 | O(n) | O(n) |
扩容 | 创建新的数组(慢) | 链表添加(快) |
Key-value的核心在于查询效率,而数组的内存为连续空间,因此查询上数组优于链表;添加操作上链表效率比数组高。
链表无法完成O(1)的查询操作,只有通过array[i],通过数组下标查询才能实现O(1)的查询效率。
问题1:如何接近O(1)的查询效率?
思路:尽量让每一个key对应一个数组下标。
问题2:不同key得到的index重复怎么解决?
思路:相同index的key都放在table[index],尽量减少index一致的情况(hash冲突)
数组形式的key-value(String-Integer)数据结构,实现接近O(1)的查询。(key-value数据小于64个)**
//三个方法:1.get() 2.put() 3.hash() public class KeyValue { private Node[] table; private int size = 0; private int capacity = 0; final private int DEFAULT_INITIAL_CAPACITY= 16; public KeyValue(){ capacity = DEFAULT_INITIAL_CAPACITY; table = new Node[capacity]; } //1.查找元素 public Integer get(String key) { //计算key的hash值,得到index int hash = hash(key); int index = hash % capacity; Node node = table[index]; while (node != null){ if(node.key.equals(key)){return node.value;} node = node.next; } return null; } //2.添加元素 public Integer put(String key,Integer value){ //计算key的hash值,得到index int hash = hash(key); int index = hash % capacity; //hash桶不存在则添加 Node node = table[index]; if (node == null) { table[index] = new Node(key,value); return value; } //存在则查询,判断更新或者添加 while (node.next != null){ if(node.key.equals(key)){ node.value = value; return value; } node = node.next; } //不存在则添加 node.next =null;尾插法 node.next = new Node(key, value); return value; } //3.hash函数(HashMap源码) private int hash(String key){ int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);//高位也参与hash值 } //存储单元 class Node{ int hash; String key; Integer value; Node next; public Node(String key, Integer value) { this.key = key; this.value = value; this.hash = hash(key); } } }
优化:
int index = hash % table.length;
//优化 table.length 始终为2的n次方
int index = hash &(table.length - 1)
HashMap的难点与核心为扩容机制。
线程不安全。
请实现HashMap中线程不安全的情况。
//账户有10000块,两个人去消费,每次1000 public class Main { public static void main(String[] args) { HashMap<String, Integer> account = new HashMap<>(); //账户充值10000元 account.put("money",10000); //两个人共享账户 Thread threadA = new Thread(new ThreadA(account)); Thread threadB = new Thread(new ThreadA(account)); threadA.start(); threadB.start(); try { threadA.join(); threadB.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("余额:" + account.get("money")); } } class ThreadA implements Runnable{ HashMap<String, Integer> map = null; public ThreadA(HashMap<String, Integer> map) { this.map = map; } @Override public void run() { for (int i = 1; i <= 10; i++) { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } if(map.get("money") > 0){ map.replace("money",map.get("money") - 1000); System.out.println(Thread.currentThread().getName()+" 消费: "+ i*1000); } } } }
结果两个人消费12000,账户初始金额10000,线程不安全。
如何解决,请看ConcurrenHashMap。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。