学习:Java 中的双列集合(HashMap、LinkedHashMap 和 TreeMap )

        在 Java 编程中,集合框架为开发者提供了多种数据结构和算法,而 `Map` 接口是其中处理键值对数据结构的核心接口。本文将深入探讨 `Map` 接口的实现类:`HashMap`、`LinkedHashMap` 和 `TreeMap`,分析它们的特点、底层原理及适用场景。

双列集合的基本概念

        双列集合与单列集合不同,它一次存储一对数据,即一个键(Key)和一个值(Value)。在双列集合中,键是唯一的,不允许重复,而值则可以重复。键和值之间形成了一个一一对应的关系,这对组合在 Java 中被称为 “键值对” 或 “Entry 对象”。

Map 的常用 API

        Map 是所有双列集合的顶层接口,提供了一系列通用的方法来操作键值对:

- 添加元素:V put(K key, V value) —— 将指定键与值关联。
- 删除元素:V remove(Object key) —— 根据键删除对应的键值对。
- 清空集合:void clear() —— 清空所有键值对。
- 判断是否包含指定键:boolean containsKey(Object key) —— 检查集合中是否存在某个键。
- 判断是否包含指定值:boolean containsValue(Object value) —— 检查集合中是否存在某个值。
- 判断集合是否为空:boolean isEmpty() —— 检查集合是否为空。
- 获取集合长度:int size() —— 返回集合中键值对的数量。

put 方法的细节:添加或覆盖

        在 Map 集合中,put 方法用于添加键值对。当键不存在时,直接将新的键值对添加到集合中并返回 null;当键已经存在时,则覆盖旧的键值对,并返回被覆盖的值。

Map<String, Integer> map = new HashMap<>();
Integer prevValue = map.put("key1", 100); // 添加新的键值对,返回 null
Integer replacedValue = map.put("key1", 200); // 覆盖键 "key1" 对应的值,返回旧值 100

HashMap

特点

        HashMap 是 Map 接口的一个常用实现类。它的特点由键的特性决定:无序、不重复、无索引。HashMap 底层的数据结构为哈希表(HashTable),与 HashSet 的底层原理几乎完全相同。

哈希值与去重机制

        HashMap 使用键的哈希值(hashCode)来快速定位存储位置。哈希值是对象的整数表现形式,用于加速查找操作。哈希值的生成依赖于对象的 hashCode 方法:

1. 如果没有重写 hashCode 方法,不同对象计算出的哈希值是不同的。
2. 如果已经重写 hashCode 方法,不同的对象只要属性值相同,计算出的哈希值就是一样的。
3. 在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样(哈希碰撞)。

当两个键的哈希值相同时,HashMap 通过链表或红黑树来解决冲突。

底层原理

1. 初始化:创建一个默认长度为 16,加载因子为 0.75 的数组,数组名为 `table`。
2. 计算哈希值:根据键的哈希值与数组长度计算出应存入的位置。
3. 存储元素:
   - 若该位置为空,则直接存入。
   - 若该位置已有元素,则依次比较链表或树中的每个节点:
     - 如果键相同,则覆盖旧值。
     - 如果键不同,则添加到链表末尾或红黑树的合适位置。

遍历方式

1. 键找值

        通过获取所有键的集合,遍历键并使用 get 方法获取对应的值。

Map<String, String> map = new HashMap<>();
map.put("key1", "value1");
map.put("key2", "value2");

Set<String> keys = map.keySet();
for (String key : keys) {
    String value = map.get(key);
    System.out.println(key + " = " + value);
}
2. 键值对遍历

        通过 entrySet 方法获取所有键值对对象,并遍历这些 Entry 对象。

Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String, String> entry : entries) {
    String key = entry.getKey();
    String value = entry.getValue();
    System.out.println(key + " = " + value);
}
3. 使用 Lambda 表达式遍历

        Lambda 表达式简化了遍历操作。

map.forEach((key, value) -> System.out.println(key + " = " + value));

LinkedHashMap

特点

        LinkedHashMap 是 HashMap 的子类,具有与 HashMap 相同的功能,但它额外维护了元素的插入顺序。有序、不重复、无索引是其显著特点。

底层原理

        LinkedHashMap 的底层数据结构是哈希表和双向链表的结合。哈希表用于存储键值对,而双向链表则用于维护键值对的插入顺序。

Map<String, String> linkedMap = new LinkedHashMap<>();
linkedMap.put("key1", "value1");
linkedMap.put("key2", "value2");
linkedMap.put("key3", "value3");

linkedMap.forEach((key, value) -> System.out.println(key + " = " + value));
// 输出顺序与插入顺序一致

TreeMap

特点

        TreeMap 是 Map 接口的另一个实现类,其特点由键的特性决定:不重复、无索引、可排序。TreeMap 内部采用红黑树(自平衡二叉搜索树)来存储键值对,并默认按键的自然顺序排序。

排序方式

TreeMap 提供了两种排序方式:1. 自然排序:键的类实现 Comparable 接口,并重写 compareTo 方法。

public class Student implements Comparable<Student> {
    private int age;

    @Override
    public int compareTo(Student o) {
        return this.age - o.age;
    }
}

2. 自定义排序:在创建 TreeMap 对象时,传入一个 Comparator 对象,指定自定义排序规则。

TreeMap<Integer,String> tm = new TreeMap<>(new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        //o1:当前要添加的元素
        //o2:表示已经在红黑树中存在的元素
        return o2 - o1;
    }
});

TreeMap<Integer, String> tm = new TreeMap<>((o1, o2) -> o2 - o1);

底层原理

        TreeMap 的底层数据结构是红黑树,红黑树是一种自平衡的二叉搜索树,能够确保所有基本操作(如插入、删除、查找)的时间复杂度为 O(log n)。

如何选择双列集合?

- 默认选择:如果没有特定需求,HashMap 是首选,因为它的效率最高。
- 需要保证存取顺序:选择 LinkedHashMap,它在 HashMap 的基础上维护了插入顺序。
- 需要排序:选择 TreeMap,它会根据键的自然顺序或自定义顺序进行排序。

总结

        本文深入探讨了 Java 中的双列集合,包括 HashMap、LinkedHashMap 和 TreeMap。这些集合在功能和性能上各有千秋,开发者应根据具体需求选择合适的集合类。理解它们的底层原理和特点,有助于编写出高效、可维护的代码。

  • 30
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值