深入剖析Java Map:掌握HashMap、TreeMap、LinkedHashMap和ConcurrentHashMap

Map接口

在Java中,Map接口是一个通用的键值对集合,它允许我们使用一对键值来存储和访问数据。可以通过put(key, value)方法将键值对存储到Map中,然后通过get(key)方法获取相应的值。

Map接口中常用的方法有:

  • put(Object key, Object value):将指定的键值对存储到Map中
  • get(Object key):获取指定键所对应的值
  • remove(Object key):从Map中删除指定键的键值对
  • keySet():返回所有键的集合
  • entrySet():返回所有键值对的集合
  • size():返回Map中包含的键值对的数量

Map的原理

Java 的Map 作为一种键值对存储数据的数据结构,在内部实现上主要依赖两种机制:哈希表和红黑树。

  1. 哈希表: 在 Java 中,哈希表的实现通常是通过一个数组加上链表或红黑树来实现的。哈希表通过将键的哈希值映射到数组索引,然后在该索引位置存储相应的值。在发生哈希冲突的情况下,即多个键映射到了相同的数组索引位置,一般会使用链表或红黑树来解决冲突。

  2. 链表: 当多个键映射到相同的数组索引时,使用链表来存储键值对,其特点是插入和查找的时间复杂度为O(1),但在查找时需要遍历链表进行线性搜索,效率较低。

  3. 红黑树: 为了解决链表查找效率低下的问题,Java 在哈希表中引入了红黑树。当链表长度超过一定阈值时,链表将被转化为红黑树,进一步提高查找的效率,红黑树能够在O(logN)的时间复杂度内完成插入、删除和查找操作。

Map 的内部实现根据这些机制,选择哈希表或红黑树来实现键值对的存储和查找,以达到高效的数据存储和访问。

  • HashMap 使用哈希表实现,通过哈希函数计算键的索引位置。其特点是存取的速度非常快,查找的时间复杂度为O(1),但是在空间上相对于其他实现稍微占用更多。
  • TreeMap 使用红黑树实现,保证了键值对的有序性。它的特点是查找的时间复杂度为O(logN),可以进行有序的遍历和范围查询。
  • LinkedHashMap 在HashMap的基础上增加了双向链表,保持了键值对的插入顺序。它的特点是除了具备HashMap的快速存取功能外,还可以按照插入的顺序进行遍历。
  • ConcurrentHashMap 是在哈希表基础上进行了加锁,通过分段锁实现多线程的并发安全性。它的特点是允许多个线程同时进行读操作,从而提高了并发读写的性能,而写操作仍然需要加锁保证原子性。

Map的初始化

先看下面的示例:

import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

public class MapInitializationExample {
    public static void main(String[] args) {

        // 初始化空的HashMap
        Map<Integer, String> hashMap = new HashMap<>();
        // 添加键值对
        hashMap.put(1, "苹果");
        hashMap.put(2, "香蕉");
        hashMap.put(3, "橙子");
        System.out.println("hashMap: " + hashMap); // 输出: {1=苹果, 2=香蕉, 3=橙子}

        // 使用Map.of()方法初始化不可变的Map
        Map<String, Integer> immutableMap = Map.of("苹果", 1, "香蕉", 2, "橙子", 3);
        System.out.println("immutableMap: " + immutableMap); // 输出: {香蕉=2, 苹果=1, 橙子=3}

        // 使用静态代码块初始化不可变Map
        Map<String, String> staticMap = new HashMap<String, String>() {
            {
                put("一个", "1");
                put("两个", "2");
                put("三个", "3");
            }
        };
        System.out.println("staticMap: " + staticMap); // 输出: {三个=3, 一个=1, 两个=2}
    }
}

以上代码展示了三种常见的方式来初始化Map:

  1. 通过put方法进行初始化:
// 初始化空的HashMap
Map<Integer, String> hashMap = new HashMap<>();
// 添加键值对
hashMap.put(1, "苹果");
hashMap.put(2, "香蕉");
hashMap.put(3, "橙子");

这种方式首先创建了一个空的HashMap对象,然后使用put方法逐个添加键值对。

  1. 使用Map.of()方法进行初始化(适用于Java 9及以上版本):
// 使用Map.of()方法初始化不可变的Map
Map<String, Integer> immutableMap = Map.of("苹果", 1, "香蕉", 2, "橙子", 3);

这种方式使用Java 9中引入的Map.of()静态方法来创建一个不可变的Map对象。通过在方法参数中指定键值对来初始化Map。

  1. 使用静态代码块进行初始化:
// 使用静态代码块初始化Map
Map<String, String> staticMap = new HashMap<String, String>() {
    {
        put("一个", "1");
        put("两个", "2");
        put("三个", "3");
    }
};

这种方式使用了匿名内部类的方式,继承HashMap并在静态代码块中使用put方法逐个添加键值对来初始化Map。

HashMap

HashMap是Java中最常用的键值对集合,它内部使用哈希表来实现键值对的存储和查找。HashMap中的键和值都可以为null值,同时HashMap是无序的。

下面是一个使用HashMap的示例:

HashMap<Integer, String> map = new HashMap<>();
map.put(1, "A");
map.put(2, "B");
map.put(3, "C");

String value = map.get(2);
System.out.println(value); // B

map.remove(3);

Set<Integer> keySet = map.keySet();
for (Integer key : keySet) {
    String val = map.get(key);
    System.out.println(key + ":" + val);
}

在这个示例中,我们使用了HashMap存储了三个键值对,并通过get()方法获取键为2的值。然后使用remove()方法删除了键为3的键值对,最后使用keySet()方法获取所有的键,并通过遍历keySet并使用get()方法获取相应的值,输出了HashMap中存储的键值对。

TreeMap

TreeMap是Java中一种有序的键值对集合,它的实现基于红黑树,因此可以保证键值对的有序性。TreeMap中的键和值都可以为null值。

下面是一个使用TreeMap的示例:

TreeMap<Integer, String> map = new TreeMap<>();
map.put(3, "C");
map.put(1, "A");
map.put(2, "B");

String value = map.get(2);
System.out.println(value); // B

map.remove(3);

Set<Integer> keySet = map.keySet();
for (Integer key : keySet) {
    String val = map.get(key);
    System.out.println(key + ":" + val);
}

在这个示例中,我们使用了TreeMap存储了三个键值对,并通过get()方法获取键为2的值。然后使用remove()方法删除了键为3的键值对,最后使用keySet()方法获取所有的键,并通过遍历keySet并使用get()方法获取相应的值,输出了TreeMap中存储的键值对。

LinkedHashMap

LinkedHashMap是Java中一种可以保证键值对顺序的键值对集合,它继承于HashMap,并使用双向链表来维护键值对的顺序。

下面是一个使用LinkedHashMap的示例:

LinkedHashMap<Integer, String> map = new LinkedHashMap<>();
map.put(3, "C");
map.put(1, "A");
map.put(2, "B");

String value = map.get(2);
System.out.println(value); // B

map.remove(3);

Set<Integer> keySet = map.keySet();
for (Integer key : keySet) {
    String val = map.get(key);
    System.out.println(key + ":" + val);
}

在这个示例中,我们使用了LinkedHashMap存储了三个键值对,并通过get()方法获取键为2的值。然后使用remove()方法删除了键为3的键值对,最后使用keySet()方法获取所有的键,并通过遍历keySet并使用get()方法获取相应的值,输出了LinkedHashMap中存储的键值对。

ConcurrentHashMap

ConcurrentHashMap是Java中一种线程安全的HashMap,它提供了与多线程环境下的高并发读写需求的性能来应对的方法。ConcurrentHashMap基于分段锁的机制,将哈希表分成多个段,每个段都可以独立加锁,从而支持多个线程同时操作不同的段,提高并发性能。

下面是一个使用ConcurrentHashMap的示例:

ConcurrentHashMap<Integer, String> map = new ConcurrentHashMap<>();
map.put(3, "C");
map.put(1, "A");
map.put(2, "B");

String value = map.get(2);
System.out.println(value); // B

map.remove(3);

Set<Integer> keySet = map.keySet();
for (Integer key : keySet) {
    String val = map.get(key);
    System.out.println(key + ":" + val);
}

在这个示例中,我们使用了ConcurrentHashMap存储了三个键值对,并通过get()方法获取键为2的值。然后使用remove()方法删除了键为3的键值对,最后使用keySet()方法获取所有的键,并通过遍历keySet并使用get()方法获取相应的值,输出了ConcurrentHashMap中存储的键值对。

关注微信公众号:“小虎哥的技术博客”。我们会定期发布关于Java技术的详尽文章,让您能够深入了解该领域的各种技巧和方法,让我们一起成为更优秀的程序员‍‍!

你可能感兴趣的:(Java系列之打好基础,java,开发语言,Map)