- 将键(key) 映射到值(value)的对象,
- 一个映射不能包含相同的key
- 一个key只能映射一个value
- 可以存储多个键值对
- 具体的实现类有: hashMap、hashtable、linkedHashMap
- 基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。 (除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映 射的顺序,特别是它不保证该顺序恒久不变。
// 创建一个映射 Mapdict = new HashMap<>(); // 向映射中添加一个键值对 dict.put(1, "张三"); dict.put(2, "李四"); dict.put(3, "王五"); // 根据key 获取 value System.out.println(dict.get(1)); System.out.println(dict.get(2)); System.out.println(dict.get(3)); // 对映射进行遍历操作
- 对映射进行遍历方法
System.out.println("--------------------------");
System.out.println("使用获取Entry对象集合的方式来遍历");
// 方法1. 获取Entry对象集合
Set> entryset = dict.entrySet();
// 集合可以使用foreach
// 每次返回的都是一个May.Entry 类型的一个对象, 这个对象主要存储 一个key, 和一个 value 两个属性
for (Map.Entry entry : entryset) {
System.out.println(entry.getKey() + "-->" + entry.getValue());
}
System.out.println("--------------------------");
System.out.println("使用获取key的集合方式来遍历");
// 方法2, 获取 key的集合
Set keySet = dict.keySet();
// for(Integer k : keySet) {
// System.out.println(k + "-->" + dict.get(k));
// }
keySet.forEach(k -> System.out.println(k + "-->" + dict.get(k)));
System.out.println("--------------------------");
System.out.println("直接获取value集合, 直接遍历");
// 方法3. 直接获取value 集合
Collection valueSet = dict.values();
valueSet.forEach( v -> System.out.println(v));
System.out.println("--------------------------");
System.out.println("直接遍历");
// 方法4. 直接forEach
dict.forEach((k, v) -> System.out.println(k + "-->" + v));
-
HashMap实现原理
-
基于哈希表(数组 + 链表 + 二叉树(红黑树) , JDK1.8后添加的二叉树
-
默认的加载因子为0.75,默认的数组(哈希表)大小为16
-
怎么将对象存储在哈希表中呢?
-
将key 通过内部的hash()方法 计算出hash值, 在通过这个值 与 数组的长度求余数, 来决定这个对象在数组中所存储的位置
如果多个对象都存储这个位置时, 那么以链表的形式进行存储, 在JDK1.8中, 当链表的大小大于8
时, 那么该链表会转换为红黑树结构进行存储。
-
-
那么这个哈希表怎么扩充的呢?
- 当数组的容量超过75%(加载因子的百分比), 那么数组需要扩充
- 扩充算法: 当前的容量 << 1位(扩大到原来的两倍),
- 注意:
- 如果扩充的次数过多, 会影响性能 , 因为每次扩充,都要将原来的hash表重新散列(每个对象重新计算新的存储位置), 所以, 尽量减少扩充带来的性能影响。
- 是线程不安全的, 建议在单线程中使用
-
public class Hashtableextends Dictionary implements Map , Cloneable, Serializable
- 此类实现一个哈希表,该哈希表将键映射到相应的值。任何非 null 对象都可以用作键或值。 为了成功地在哈希表中存储和获取对象,用作键的对象必须实现 hashCode 方法和 equals 方法。
- 基本使用
// 创建一个hashtable Hashtabledict = new Hashtable<>(); // 向映射中添加一个键值对 dict.put(1, "张三"); dict.put(2, "李四"); dict.put(3, "王五"); // 遍历 dict.forEach((k, v) -> System.out.println(k + "->>" + v));
- 实现原理
- 基于hash表(数组 + 链表)实现
- 默认数组的大小位11, 加载因子为0.75
- 数组扩充算法:原数组 << 1 + 1 (扩大到原来的两倍 + 1)
- 是线程安全的, 建议使用在多线程中
public class linkedHashMapextends HashMap implements Map
- Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。此实现与 HashMap 的不同之处 在于,后者维护着一个运行于所有条目的双重链接列表。
- 基本使用
// 创建linkedHashMap 实例 linkedHashMapdict = new linkedHashMap<>(); dict.put(1, "张三"); dict.put(2, "李四"); dict.put(3, "王五"); // 遍历 dict.forEach((k, v) -> System.out.println(k + "->>" + v));
- 实现原理
- 因为它继承与 HashMap, 所以底层原理差不多
- 那么与 HashMap的区别呢?
- 由于HashMap 不能保证顺序恒久不变, 此类使用了一个双重链接表来维护元素添加的顺序。
- 基于红黑树(Red-Black tree)的 NavigableMap 实现。该映射根据其键的自然顺序进行排序, 或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。
public class TreeMapextends AbstractMap implements NavigableMap , Cloneable, Serializable
- 使用
// 创建TreeMap实例 TreeMapdict = new TreeMap<>(); dict.put(new Dog2("wangwang", 2, 1), "dog1"); dict.put(new Dog2("erha", 3, 1), "dog2"); dict.put(new Dog2("keji", 2, 3), "dog3"); dict.forEach((k, v) -> System.out.println(k + "-->" + v));
- 实现原理
- 基于二叉树的红黑数实现
- 存储数据时, 当key 相同时,key不会变, 而value会被覆盖
HashMapmap = new HashMap<>(); // 添加 map.put(1, "张三"); // 如果不存在则添加 map.putIfAbsent(2, "李四"); map.putIfAbsent(3, "王五"); map.putIfAbsent(4, "赵六"); map.putIfAbsent(5, "王二麻子"); map.forEach((k, v) -> System.out.println(k + "->>" + v)); // 获取值, 如果不存在,则返回提供的默认值 System.out.println(map.getOrDefault(100, "null")); System.out.println("------------------111"); // 删除值, 可以根据key来删除, 也可以根据key + value 来删除 map.remove(1); // 删除成功 // param1: 操作的key, param2: key的值 map.remove(5, "赵六"); // key为5 ,但value 不为 '赵六', 没有删除 map.forEach((k, v) -> System.out.println(k + "->>" + v)); System.out.println("----------------222"); // param1: 要操作的key, param2: 要替换的值 map.replace(5, "王三麻子"); // param1: 要操作的key, param2: 旧值, param3: 新值 map.replace(4, "赵六", "赵7"); map.forEach((k, v) -> System.out.println(k + "->>" + v)); System.out.println("--------------------333"); // param1: 要操作的key, param2: lambda表达式, 返回值就是key 的新值 map.compute(5, (k, oldV) -> oldV + "牛了b了"); map.forEach((k, v) -> System.out.println(k + "->>" + v)); System.out.println("-----------------------4444"); // 如果存在则修改 map.computeIfPresent(2, (k, oldV) -> oldV + "n l b l"); map.forEach((k, v) -> System.out.println(k + "->>" + v)); System.out.println("-----------------------555"); // 如果不存在则修改 map.computeIfAbsent(10, (v) -> "jbn"); map.forEach((k, v) -> System.out.println(k + "->>" + v));



