Java 有没有更好的方法从映射中检索值

Java 有没有更好的方法从映射中检索值,java,collections,lambda,guava,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 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());
}
Java6中,有没有更好的方法不使用for循环来获取Id

根据Java8,引用代码形式@rohit jain answer

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

在java6中有这样的考虑吗?

在某一点上,您不能逃避映射上的迭代。 对于映射,最有效的方法是迭代“entrySet()”

不确定效率(因为它不会影响太多),但如果您想使用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
final List<String> list =
    FluentIterable.from(map.values()).transform(new ExtractLocationId()).toList();
编辑:该问题已被修改,现在似乎不再涉及效率。现在,这个答案已经不合适了,但现在,我把它留在这里,也许有人会觉得它有用

首先是一个一般性的提示:你说

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

for (final String string : list) {
    System.out.println(string);
}
然而,你应该而不是期望那样。HashMap没有以任何方式排序。elments的顺序可能会有所不同。同样重要的是:如果您在地图中添加了更多元素,那么先前包含在地图中的元素的顺序可能会改变

如果希望迭代期间元素的顺序与插入顺序相同,则应使用
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;
    }
}
同样,这不应该被认为是理所当然的,除非它通过一个合适的Micobenchmarking框架进行验证,但坦率地说:
LinkedHashMap
速度快了30%,要么给,要么拿,我怀疑Micobenchmarking框架会告诉我相反的情况



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

如果您喜欢使用兼容Java 6的版本,那么您可以使用Guava及其接口:

public class ExtractLocationId implements Function<Location, String> {
     @Override
     public String apply(final Location location) {
          return location.getId();
     }
}
公共类ExtractLocationId实现函数{
@凌驾
公共字符串应用(最终位置){
返回location.getId();
}
}
然后像这样使用它:

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
final List<String> list =
    FluentIterable.from(map.values()).transform(new ExtractLocationId()).toList();
最终列表=
FluentIterable.from(map.values()).transform(new-ExtractLocationId()).toList();
它需要比Java 8 Lambda版本更多的代码(由于它自己的
函数
实现),但它支持较旧的Java版本。

如果使用,可以编写以下代码:

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);
MutableMap=Maps.mutable.empty();
地图放置(“1”,新位置(“a”,空));
地图放置(“2”,新位置(“b”,空));
地图放置(“3”,新位置(“c”,空));
List=map.collect(位置::getId.toSortedList();
Bag Bag=地图收集(位置::getId);
Assert.assertEquals(list.mutable.with(“a”、“b”、“c”),list);
Assert.assertEquals(Bags.mutable.with(“a”、“b”、“c”),bag);
以下代码也适用于Java5-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);
Function=new Function()
{
公共字符串值(位置)
{
返回location.getId();
}
};
List=map.collect(函数).toSortedList();
袋子=地图收集(功能);
Assert.assertEquals(list.mutable.with(“a”、“b”、“c”),list);
Assert.assertEquals(Bags.mutable.with(“a”、“b”、“c”),bag);

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

您能更具体地说一下您所说的高效吗?在某种程度上,如果要打印条目,您需要迭代映射中的条目。现在有办法解决这个问题。另外,请注意,结果很可能是c、a、b或b、a、c或任何其他组合。为了澄清,效率意味着我可以去掉第一个For循环。我知道我需要打印第二张,这是不可避免的。不。没有更好的方法了。
for(最终位置s:map.values()){System.out.println(s.getId());}
。如果您只想打印值,则无需创建临时列表。如果OP不关心键,为什么迭代entrySet比迭代值更有效?由于整体性能,迭代映射项的速度比迭代键或值更快。@Dae您能证明这一点吗?我的意思是,当谈到效率时,我不相信任何文字。@Dae你应该阅读源代码。您会发现,值上的迭代器与条目上的迭代器相同,只是它返回的是值而不是条目。您以秒赢得了我!;)也许终端操作可以是forEach(System.out::println)。这样,当OP请求时,只会发生1次
。@Magnamag补充说:)打印是次要的,我知道即使使用lambdas,也会发生至少1次值传递,我的问题是,我能否像在java8中那样在java6中更简洁地编写此代码。如果问题是这样,我道歉misleading@SDS在Java6中,没有。这是您将获得的最好结果。