Java中的Map是一种以键值对形式存储数据的容器,常用的实现类有HashMap、LinkedHashMap和TreeMap。遍历Map是我们在使用Map时最常遇到的问题之一,下面介绍三种常用的遍历方法。
1. 通过entrySet遍历
Map map = new HashMap<>(); map.put(1, "one"); map.put(2, "two"); map.put(3, "three"); for (Map.Entry entry : map.entrySet()) { System.out.println(entry.getKey() + " " + entry.getValue()); }
使用entrySet遍历时,先获取到Map的所有entry集合,然后遍历所有entry,通过entry的getKey和getValue方法获取键值对。
2. 通过keySet遍历
Map map = new HashMap<>(); map.put(1, "one"); map.put(2, "two"); map.put(3, "three"); for (Integer key : map.keySet()) { System.out.print(key + " "); System.out.println(map.get(key)); }
使用keySet遍历时,先获取到Map的所有key集合,然后遍历所有key,通过Map的get方法获取对应的值。
3. 通过values遍历
Map map = new HashMap<>(); map.put(1, "one"); map.put(2, "two"); map.put(3, "three"); for (String value : map.values()) { System.out.println(value); }
使用values遍历时,直接获取到Map的所有值的集合,然后遍历所有值。
有序Map是指像LinkedHashMap和TreeMap这样的实现类,能够按照某种顺序(通常是插入顺序或按照键的自然顺序)存储键值对。如何遍历这些有序Map呢?
其实原理和遍历HashMap一样,只是在创建实例时需要指定排序的方式。
1. LinkedHashMap
Map map = new LinkedHashMap<>(16, 0.75f, true); map.put(1, "one"); map.put(2, "two"); map.put(3, "three"); for (Map.Entry entry : map.entrySet()) { System.out.println(entry.getKey() + " " + entry.getValue()); }
在创建LinkedHashMap时,需要传入三个参数:initialCapacity(初始化容量)、loadFactor(负载因子)和accessOrder(排序方式)。accessOrder为true时表示按照访问顺序排序,为false时表示按照插入顺序排序。
2. TreeMap
Map map = new TreeMap<>(); map.put(1, "one"); map.put(2, "two"); map.put(3, "three"); for (Map.Entry entry : map.entrySet()) { System.out.println(entry.getKey() + " " + entry.getValue()); }
在创建TreeMap时,只需要像创建普通HashMap一样传入默认的初始化容量即可。
不同的遍历方式对Map的性能有影响,下面通过实验来看看它们之间的性能差异。
实验代码如下:
Map map = new HashMap<>(); Random random = new Random(); for (int i = 0; i < 1000000; i++) { map.put(random.nextInt(1000000), "some value"); } long start = System.currentTimeMillis(); for (Map.Entry entry : map.entrySet()) { // do nothing } long end = System.currentTimeMillis(); System.out.println("遍历entrySet耗时:" + (end - start) + "ms"); start = System.currentTimeMillis(); for (Integer key : map.keySet()) { // do nothing } end = System.currentTimeMillis(); System.out.println("遍历keySet耗时:" + (end - start) + "ms"); start = System.currentTimeMillis(); for (String value : map.values()) { // do nothing } end = System.currentTimeMillis(); System.out.println("遍历values耗时:" + (end - start) + "ms");
实验结果(遍历1000000个元素,单位:毫秒):
遍历entrySet耗时:14ms
遍历keySet耗时:16ms
遍历values耗时:33ms
从实验结果来看,通过entrySet遍历Map是最快的,而通过values遍历是最慢的。
https://www.10zhan.com
在多线程环境下使用Map时,遍历是一个非常常见的操作,但是需要注意线程安全问题。
如果只是读取操作,可以直接使用Java 8中的forEach方法,示例代码如下:
Map map = new ConcurrentHashMap<>(); map.put(1, "one"); map.put(2, "two"); map.put(3, "three"); map.forEach((key, value) -> { System.out.println(key + " " + value); });
如果同时存在读取和写入操作,可以使用并发安全的遍历方式,如下代码所示:
Map map = new ConcurrentHashMap<>(); map.put(1, "one"); map.put(2, "two"); map.put(3, "three"); map.entrySet().parallelStream().forEach(entry -> { System.out.println(entry.getKey() + " " + entry.getValue()); });
使用parallelStream遍历Map时,底层会自动实现并发安全,处理速度也会更快。