1. 内容大纲
本章节代码:https://github.com/wicksonZhang/data-structure/tree/main/10-Map
2. 基础知识
2.1. Map 解决了什么问题?
例如,我们在全国的行政区划表中,需要通过 行政区划 Code 找到对应的 Name 名称 , 如果使用的是数组进行存储的话,我们需要找到某个元素,可能是从头找到尾,时间复杂度可能为O(n),这会导致效率会特别低。
如果使用数组实现代码如下
- 如果我们底层采用的数组,最好的情况可能是 O(1),最坏的情况是 O(n),平均时间复杂度为 O(n)
1 2 3 4 5 6 7 8 9 10 11 12
| List<DivisionInfo> divisionInfos = new ArrayList<>(); divisionInfos.add(new DivisionInfo(110000 "北京")); divisionInfos.add(new DivisionInfo(120000, "天津")); divisionInfos.add(new DivisionInfo(130000, "河北省")); String name = ""; divisionInfos.forEach(divisionInfo -> { if (11000 == divisionInfo.getNumberCode()) { name = divisionInfo.getName(); break; } });
|
如果使用映射Map实现的代码如下
- 如果我们底层采用的红黑树,最好的情况O(logn),最坏的情况O(logn)
1 2 3 4 5 6
| Map<Integer, DivisionInfo> map = new HashMap<>(); map.put(110000, new DivisionInfo(110000 "北京")); map.put(120000, new DivisionInfo(120000, "天津")); map.put(130000, new DivisionInfo(130000, "河北省"));
DivisionInfo divisionInfo = map.get(110000);
|
2.2. Map 是什么?
在数据结构中,映射(Mapping)是一种将键(Key)与值(Value)关联起来的结构,也被称为字典、哈希表或关联数组,具体的实现方式有很多种。
2.3. Map 优缺点
优点
- 快速查询: Map提供了通过键直接访问值的机制,因此在查找特定元素时具有很高的效率。在Hash情况下,查找操作的时间复杂度是常量级别(O(1))。在红黑树的情况下,查找操作的时间复杂度是对数级别(O(logn))
- 唯一性: Map中的键必须是唯一的,这确保了每个键都对应一个唯一的值。这在需要建立唯一关联关系的场景下非常有用。
- 灵活性: Map适用于各种数据关联问题,可以用于构建字典、缓存、配置表等多种应用。不同实现方式(如哈希表、红黑树)可以满足不同的需求。
缺点
- 哈希冲突: 在使用哈希表实现的Map中,可能会发生哈希冲突,即不同的键映射到相同的哈希桶。
- 顺序不确定: Map通常不保证元素的顺序,这在一些情况下可能是一个缺点。
- 复杂性: 某些Map的实现(例如红黑树)相对复杂,可能需要更多的计算资源和时间来维护数据结构的平衡性。
3. Map 接口设计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public interface Map<K, V> {
int size();
boolean isEmpty();
void clear();
V put(K key, V value);
V get(K key);
V remove(K key);
boolean containKey(K key);
boolean containValue(V value);
void traversal(Visitor<K, V> visitor);
public static abstract class Visitor<K, V> { public boolean stop;
public abstract boolean visit(K key, V value); }
}
|
4. Map 代码实现
4.1. Node节点初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
|
public class TreeMap<K, V> implements Map<K, V> { private static final boolean RED = false;
private static final boolean BLACK = true;
private static class Node<K, V> { K key;
V value;
boolean color = RED;
Node<K, V> leftNode;
Node<K, V> rightNode;
Node<K, V> parentNode;
public Node(K key, V value, Node<K, V> parentNode) { this.key = key; this.value = value; this.parentNode = parentNode; }
public boolean isLeaf() { return leftNode == null && rightNode == null; }
public boolean hasTwoChildren() { return leftNode != null && rightNode != null; }
public boolean isLeftChild() { return parentNode != null && this == parentNode.leftNode; }
public boolean isRightChild() { return parentNode != null && this == parentNode.rightNode; }
public Node<K, V> sibling() { if (isLeftChild()) { return parentNode.rightNode; } if (isRightChild()) { return parentNode.leftNode; } return null; }
}
}
|
4.2. 变量初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class TreeMap<K, V> implements Map<K, V> {
private int size;
protected Node<K, V> root;
private final Comparator<? super K> comparator;
public TreeMap() { this(null); }
public TreeMap(Comparator<? super K> comparator) { this.comparator = comparator; } }
|
4.3. 元素数量
1 2 3 4 5 6 7 8
|
@Override public int size() { return size; }
|
4.4. 是否为空
1 2 3 4 5 6 7 8 9
|
@Override public boolean isEmpty() { return size == 0; }
|
4.5. 清除所有元素
1 2 3 4 5 6 7 8
|
@Override public void clear() { size = 0; root = null; }
|
4.6. 添加元素
- 添加元素主要是基于红黑树,所以如下代码会涉及到相关红黑树代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
|
@Override public V put(K key, V value) { keyNotNullCheck(key);
if (root == null) { root = new Node<>(key, value, null); size++; afterPut(root); return null; }
Node<K, V> node = root; Node<K, V> parent = root; int cmp = 0; while (node != null) { cmp = compare(key, node.key); parent = node; if (cmp > 0) { node = node.rightNode; } else if (cmp < 0) { node = node.leftNode; } else { node.key = key; V oldValue = node.value; node.value = value; return oldValue; } }
Node<K, V> newNode = new Node<>(key, value, parent); if (cmp > 0) { parent.rightNode = newNode; } else { parent.leftNode = newNode; } size++; afterPut(newNode); return null; }
private void afterPut(Node<K, V> node) { Node<K, V> parentNode = node.parentNode; if (parentNode == null) { black(node); return; }
if (isBlack(parentNode)) { return; }
Node<K, V> uncle = parentNode.sibling(); Node<K, V> grandNode = red(parentNode.parentNode); if (isRed(uncle)) { black(parentNode); black(uncle); afterPut(grandNode); return; }
if (parentNode.isLeftChild()) { if (node.isLeftChild()) { black(parentNode); } else { black(node); rotateLeft(parentNode); } rotateRight(grandNode); } else { if (node.isLeftChild()) { black(node); rotateRight(parentNode); } else { black(parentNode); } rotateLeft(grandNode); } }
|
4.7. 获取元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
@Override public V get(K key) { Node<K, V> node = node(key); return node != null ? node.value : null; }
private Node<K, V> node(K key) { if (key == null) { return null; } Node<K, V> node = this.root; while (node != null) { int compare = compare(key, node.key); if (compare > 0) { node = node.rightNode; } else if (compare < 0) { node = node.leftNode; } else { return node; } } return null; }
|
4.8. 删除元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
|
@Override public V remove(K key) { return remove(node(key)); }
private V remove(Node<K, V> node) { if (node == null) { return null; } V oldValue = node.value; if (node.hasTwoChildren()) { Node<K, V> predecessorNode = predecessor(node); node.key = predecessorNode.key; node.value = predecessorNode.value; node = predecessorNode; }
Node<K, V> removeNode = node.leftNode != null ? node.leftNode : node.rightNode;
if (removeNode != null) { removeNode.parentNode = node.parentNode; if (removeNode.parentNode == null) { root = removeNode; } else if (node == node.parentNode.leftNode) { node.parentNode.leftNode = removeNode; } else { node.parentNode.rightNode = removeNode; } afterRemove(removeNode); } else if (node.parentNode == null) { root = null; afterRemove(node); } else { if (node == node.parentNode.leftNode) { node.parentNode.leftNode = null; } else { node.parentNode.rightNode = null; } afterRemove(node); } size--; return oldValue; }
|
4.9. 是否包含 Key
1 2 3 4 5 6 7 8 9 10
|
@Override public boolean containKey(K key) { return node(key) != null; }
|
4.10. 是否包含Value
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
@Override public boolean containValue(V value) { Queue<Node<K, V>> queue = new LinkedList<>(); queue.offer(root); while (!queue.isEmpty()) { Node<K, V> node = queue.poll(); if (Objects.equals(value, node.value)) return true; if (node.leftNode != null) { queue.offer(node.leftNode); } if (node.rightNode != null) { queue.offer(node.rightNode); } } return false; }
|
4.11. 单元测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| public class TreeMapTest {
private TreeMap<Integer, String> treeMap;
@BeforeEach public void setUp() { treeMap = new TreeMap<>(); treeMap.put(5, "five"); treeMap.put(3, "three"); treeMap.put(8, "eight"); treeMap.put(2, "two"); treeMap.put(4, "four"); }
@Test public void testSize() { Assertions.assertEquals(5, treeMap.size()); }
@Test public void testIsEmpty() { Assertions.assertFalse(treeMap.isEmpty()); }
@Test public void testClear() { treeMap.clear(); Assertions.assertEquals(0, treeMap.size()); Assertions.assertTrue(treeMap.isEmpty()); }
@Test public void testPut() { Assertions.assertNull(treeMap.put(1, "one")); Assertions.assertEquals(6, treeMap.size());
Assertions.assertEquals("one", treeMap.put(1, "newOne")); Assertions.assertEquals(6, treeMap.size()); }
@Test public void testGet() { Assertions.assertEquals("five", treeMap.get(5)); Assertions.assertEquals("three", treeMap.get(3)); Assertions.assertNull(treeMap.get(10)); }
@Test public void testRemove() { Assertions.assertEquals("five", treeMap.remove(5)); Assertions.assertNull(treeMap.get(5)); Assertions.assertEquals(4, treeMap.size()); }
@Test public void testContainKey() { Assertions.assertTrue(treeMap.containKey(3)); Assertions.assertFalse(treeMap.containKey(10)); }
@Test public void testContainValue() { Assertions.assertTrue(treeMap.containValue("eight")); Assertions.assertFalse(treeMap.containValue("ten")); }
@Test public void testTraversal() { class TestVisitor extends Map.Visitor<Integer, String> { final StringBuilder result = new StringBuilder(); @Override public boolean visit(Integer key, String value) { result.append(key).append(":").append(value).append(" "); return false; } }
TestVisitor visitor = new TestVisitor(); treeMap.traversal(visitor);
Assertions.assertEquals("2:two 3:three 4:four 5:five 8:eight ", visitor.result.toString()); } }
|
5. 实战练习
5.1. 文件打印
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
|
public class ComparisonTest {
@Test public void treeMapTest() { Times.test("treeMapTest", () -> common(new TreeMap<>(), file())); }
public String[] file() { FileInfo fileInfo = Files.read("D:\\java\\jdk-8\\jdk1.8.0_211\\src\\java\\util", new String[]{"java"});
System.out.println("文件数量:" + fileInfo.getFiles()); System.out.println("代码行数:" + fileInfo.getLines()); String[] words = fileInfo.words(); System.out.println("单词数量:" + words.length); return words; }
public void common(TreeMap<String, Integer> treeMap, String[] words) { for (String word : words) { Integer count = treeMap.get(word); Integer totalCount = count == null ? 1 : count + 1; treeMap.put(word, totalCount); } treeMap.traversal(new Map.Visitor<String, Integer>() { @Override public boolean visit(String key, Integer value) { return false; } }); for (String word : words) { treeMap.containKey(word); } for (String word : words) { treeMap.remove(word); } }
}
|
1 2 3 4 5 6 7
| 【treeMapTest】 开始:20:13:29.446 文件数量:364 代码行数:212580 单词数量:879293 结束:20:13:30.896 耗时:1.45秒
|
6. 参考博文