是否有更好的方法从地图中检索值

问题描述 投票:2回答:5

我有以下代码

final Map<String, Location> map = new HashMap<>();
map.put("1", new Location("a", null));
map.put("2", new Location("b", null));
map.put("3", new Location("c", null));

final List<String> list = new ArrayList<>();

for (final Location s : map.values()) {
    list.add(s.getId());
}

当我打印列表时,结果是a,b,c(按预期)。

for (final String string : list) {
    System.out.println(string);
}

是否有一种更好的方法来获取ID,而无需使用Java6中的for循环。

根据Java8,引用代码形式@ rohit-jain答案

final List<String> list = map.values().stream().map(loc -> loc.getId()).collect(Collectors.toList());

java6中有什么要注意的地方吗?

java collections lambda guava
5个回答
3
投票

不确定效率(因为它影响不大),但是如果您想使用lambda做到这一点,它可能像这样:

final Map<String, Location> locationMap = new HashMap<>();
locationMap.put("1", new Location("a", null));
locationMap.put("2", new Location("b", null));
locationMap.put("3", new Location("c", null));

final List<String> list = locationMap.values().stream()
                             .map(loc -> loc.getId())
                             .collect(Collectors.toList());

System.out.println(list);  // can be `[a, b, c]` or `[b, c, a]`, etc

但是,只是因为您在这里看不到for循环,并不意味着它没有在映射的值上进行迭代。它可以,但是只是隐藏了迭代逻辑。

或者,如果您只想打印值(一次使用),甚至可以避免在此处创建列表:

locationMap.values().stream().map(loc -> loc.getId())
                    .forEach(System.out::println);

3
投票

编辑:该问题已修改,现在似乎不再涉及效率。现在,这个答案真的不再合适了,但是现在,我将其留在这里,也许有人觉得它很有帮助

首先是一般性提示:您说过

当我打印列表时,结果是a,b,c(按预期)。

但是,您应该不是期望如此。 HashMap不会以任何方式排序。元素的顺序可以不同。同样重要的是:如果在地图中添加了[[more元素,则地图中先前包含的元素的顺序可能是change

如果您希望元素在迭代过程中的顺序与插入顺序相同,则应使用LinkedHashMap而不是HashMap。它保留了迭代顺序,在那里,您对输出的期望将得到满足。

有趣的是,这引起了有关性能的问题:

LinkedHashMap上的迭代实际上比(Map上的迭代更快)。这是一个小型的微基准测试,与往常一样,应与一粒盐一起服用:

import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.Random; public class MapIteration { public static void main(String[] args) { long sum = 0; for (int size=100000; size<=1000000; size += 100000) { Map<String, Integer> mapA = new HashMap<String, Integer>(); fillMap(mapA, size); Map<String, Integer> mapB = new LinkedHashMap<String, Integer>(); fillMap(mapB, size); int runs = 100; long beforeA = System.nanoTime(); for (int i=0; i<runs; i++) { sum += computeValueSum(mapA); } long afterA = System.nanoTime(); double durationA = (afterA - beforeA) / 1e6; long beforeB = System.nanoTime(); for (int i=0; i<runs; i++) { sum += computeValueSum(mapB); } long afterB = System.nanoTime(); double durationB = (afterB - beforeB) / 1e6; System.out.printf( "Normal size %10d duration %10.3f\n", size, durationA); System.out.printf( "Linked size %10d duration %10.3f\n", size, durationB); } System.out.println(sum); } private static void fillMap(Map<String, Integer> map, int n) { Random random = new Random(0); for (int i=0; i<n; i++) { map.put(String.valueOf(i), random.nextInt(n)); } } private static long computeValueSum(Map<?, Integer> map) { long sum = 0; for (Integer i : map.values()) { sum += i; } return sum; } }

对我来说,它打印...

... Normal size 1000000 duration 2611,288 Linked size 1000000 duration 1796,030

[同样,除非经过适当的Micobenchmarking框架验证,否则不应认为这是理所当然的,但坦率地说:LinkedHashMap的速度要快30%,或多或少,我怀疑Micobenchmarking框架会告诉我相反。


通常,我基本上总是

使用LinkedHashMap而不是普通的HashMap。但不是因为性能,而是主要是因为一致的迭代顺序。关于性能:LinkedHashMap中的插入和删除可能比HashMap中的插入和删除稍微贵一点,但是这些性能差异在大多数情况下可以忽略不计。

3
投票
如果使用Eclipse Collections,则可以编写以下内容:

MutableMap<String, Location> map = Maps.mutable.empty(); map.put("1", new Location("a", null)); map.put("2", new Location("b", null)); map.put("3", new Location("c", null)); List<String> list = map.collect(Location::getId).toSortedList(); Bag<String> bag = map.collect(Location::getId); Assert.assertEquals(Lists.mutable.with("a", "b", "c"), list); Assert.assertEquals(Bags.mutable.with("a", "b", "c"), bag);

以下代码也适用于Java 5-7:

Function<Location, String> function = new Function<Location, String>() { public String valueOf(Location location) { return location.getId(); } }; List<String> list = map.collect(function).toSortedList(); Bag<String> bag = map.collect(function); Assert.assertEquals(Lists.mutable.with("a", "b", "c"), list); Assert.assertEquals(Bags.mutable.with("a", "b", "c"), bag);

注意:我是Eclipse集合的提交者


2
投票
如果您想使用Java 6兼容版本,则可以使用Guava及其Function接口:

Function

并像这样使用它:

public class ExtractLocationId implements Function<Location, String> { @Override public String apply(final Location location) { return location.getId(); } }

[它需要比Java 8 Lambda版本更多的代码(由于自己的final List<String> list =
    FluentIterable.from(map.values()).transform(new ExtractLocationId()).toList();
实现,但它支持较旧的Java版本。

0
投票
您有时无法在地图上回避迭代。对于地图,最有效的方法是遍历“ entrySet()”。
© www.soinside.com 2019 - 2024. All rights reserved.