有没有更好的方法从Map中检索值

时间:2015-03-13 20:21:59

标签: java collections lambda guava

我有以下代码

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 answer

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

java6中有什么这个构思吗?

5 个答案:

答案 0 :(得分:3)

不确定效率(因为它不会影响太多),但如果你想使用lambdas这样做,它可以是这样的:

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);

答案 1 :(得分:3)

  编辑:问题被修改了,现在似乎不再提及效率了。现在,这个答案不再适用了,但就目前而言,我会把它留在这里,也许有人觉得它很有帮助

首先提出一般性提示:你说

  

当我打印列表时,结果是a,b,c(如预期的那样)。

但是,你应该期望。 HashMap未按任何方式排序。元素的顺序可能不同。同样重要的是:如果您将更多元素添加到地图中,那么先前包含在地图中的元素的顺序可以更改

如果您希望迭代期间元素的顺序与插入顺序相同,则应使用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更贵,但在大多数情况下,这些性能差异可以忽略不计。

答案 2 :(得分:3)

如果您使用GS 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(FastList.newListWith("a", "b", "c"), list);
Assert.assertEquals(HashBag.newBagWith("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(FastList.newListWith("a", "b", "c"), list);
Assert.assertEquals(HashBag.newBagWith("a", "b", "c"), bag);

注意:我是GS Collections的开发人员

答案 3 :(得分:2)

如果您想使用Java 6兼容版本,那么您可以使用Guava及其Function界面:

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

并像这样使用它:

final List<String> list =
    FluentIterable.from(map.values()).transform(new ExtractLocationId()).toList();

它需要的代码多于Java 8 Lambda版本(由于自己的Function实现),但它支持较旧的Java版本。

答案 4 :(得分:0)

你不能在某个时刻逃避地图上的迭代。 对于地图,最有效的方法是迭代&#34; entrySet()&#34;。