一、 Map
1.1 Map 接口
在 Java 中, Map 提供了键——值的映射关系。映射不能包含重复的键,并且每个键只能映射到一个值。
以 Map 键——值映射为基础,java.util 提供了 HashMap(最常用)、 TreeMap、Hashtble、LinkedHashMap 等数据结构。
衍生的几种 Map 的主要特点:
HashMap:最常用的数据结构。键和值之间通过 Hash函数 来实现映射关系。当进行遍历的 key 是无序的
TreeMap:使用红黑树构建的数据结构,因为红黑树的原理,可以很自然的对 key 进行排序,所以 TreeMap 的 key 遍历时是默认按照自然顺序(升序)排列的。
LinkedHashMap: 保存了插入的顺序。遍历得到的记录是按照插入顺序的。
1.2 Hash 散列函数
Hash (散列函数)是把任意长度的输入通过散列算法变换成固定长度的输出。Hash 函数的返回值也称为 哈希值 哈希码 摘要或哈希。Hash作用如下图所示:
Hash 函数可以通过选取适当的函数,可以在时间和空间上取得较好平衡。
解决 Hash 的两种方式:拉链法和线性探测法
1.3 键值关系的实现
interface Entry<K,V>
在 HashMap 中基于链表的实现
static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Node<K,V> next; Node(int hash, K key, V value, Node<K,V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; }
用树的方式实现:
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> { TreeNode<K,V> parent; // red-black tree links TreeNode<K,V> left; TreeNode<K,V> right; TreeNode<K,V> prev; // needed to unlink next upon deletion boolean red; TreeNode(int hash, K key, V val, Node<K,V> next) { super(hash, key, val, next); }
1.4 Map 约定的 API
1.4.1 Map 中约定的基础 API
基础的增删改查:
int size(); // 返回大小 boolean isEmpty(); // 是否为空 boolean containsKey(Object key); // 是否包含某个键 boolean containsValue(Object value); // 是否包含某个值 V get(Object key); // 获取某个键对应的值 V put(K key, V value); // 存入的数据 V remove(Object key); // 移除某个键 void putAll(Map<? extends K, ? extends V> m); //将将另一个集插入该集合中 void clear(); // 清除 Set<K> keySet(); //获取 Map的所有的键返回为 Set集合 Collection<V> values(); //将所有的值返回为 Collection 集合 Set<Map.Entry<K, V>> entrySet(); // 将键值对映射为 Map.Entry,内部类 Entry 实现了映射关系的实现。并且返回所有键值映射为 Set 集合。 boolean equals(Object o); int hashCode(); // 返回 Hash 值 default boolean replace(K key, V oldValue, V newValue); // 替代操作 default V replace(K key, V value);
1.4.2 Map 约定的较为高级的 API
default V getOrDefault(Object key, V defaultValue); //当获取失败时,用 defaultValue 替代。 default void forEach(BiConsumer<? super K, ? super V> action) // 可用 lambda 表达式进行更快捷的遍历 default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function); default V putIfAbsent(K key, V value); default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction); default V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction); default V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction)
1.4.3 Map 高级 API 的使用
getOrDefault() 当这个通过 key获取值,对应的 key 或者值不存在时返回默认值,避免在使用过程中 null 出现,避免程序异常。
ForEach() 传入 BiConsumer 函数式接口,表达的含义其实和 Consumer 一样,都 accept 拥有方法,只是 BiConsumer 多了一个 andThen() 方法,接收一个BiConsumer接口,先执行本接口的,再执行传入的参数的 accept 方法。
Map<String, String> map = new HashMap<>(); map.put("a", "1"); map.put("b", "2"); map.put("c", "3"); map.put("d", "4"); map.forEach((k, v) -> { System.out.println(k+"-"+v); }); }
更多的函数用法:
https://www.cnblogs.com/king0/p/runoob.com/java/java-hashmap.html
1.5 从 Map 走向 HashMap
HashMap 是 Map的一个实现类,也是 Map 最常用的实现类。
1.5.1 HashMap 的继承关系
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
在 HashMap 的实现过程中,解决 Hash冲突的方法是拉链法。因此从原理来说 HashMap 的实现就是 数组 + 链表(数组保存链表的入口)。当链表过长,为了优化查询速率,HashMap 将链表转化为红黑树(数组保存树的根节点),使得查询速率为 log(n),而不是链表的 O(n)。