Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/319.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java8流:基于不同属性多次映射同一对象_Java_Lambda_Functional Programming_Java 8_Collectors - Fatal编程技术网

Java8流:基于不同属性多次映射同一对象

Java8流:基于不同属性多次映射同一对象,java,lambda,functional-programming,java-8,collectors,Java,Lambda,Functional Programming,Java 8,Collectors,我的一位同事向我提出了一个有趣的问题,我找不到一个简洁漂亮的Java8解决方案。问题是通过POJO列表进行流式传输,然后根据多个属性在映射中收集它们-映射会导致POJO多次出现 想象一下下面的POJO: private static class Customer { public String first; public String last; public Customer(String first, String last) { this.firs

我的一位同事向我提出了一个有趣的问题,我找不到一个简洁漂亮的Java8解决方案。问题是通过POJO列表进行流式传输,然后根据多个属性在映射中收集它们-映射会导致POJO多次出现

想象一下下面的POJO:

private static class Customer {
    public String first;
    public String last;

    public Customer(String first, String last) {
        this.first = first;
        this.last = last;
    }

    public String toString() {
        return "Customer(" + first + " " + last + ")";
    }
}
将其设置为
列表

备选方案2:创建地图条目并对其进行流式处理,然后
flatMap
。在我看来,这本书有点太冗长,不太容易阅读

// Alt 2: A bit verbose and "new AbstractMap.SimpleEntry" feels as
// a "hard" dependency to AbstractMap
Map<String, Customer> res2 =
        customers.stream()
                .map(p -> {
                    Map.Entry<String, Customer> firstEntry = new AbstractMap.SimpleEntry<>(p.first, p);
                    Map.Entry<String, Customer> lastEntry = new AbstractMap.SimpleEntry<>(p.last, p);
                    return Stream.of(firstEntry, lastEntry);
                })
                .flatMap(Function.identity())
                .collect(Collectors.toMap(
                        Map.Entry::getKey, Map.Entry::getValue));
如果上面的代码是这样打印的:

System.out.println(res1);
System.out.println(res2);
System.out.println(res3);
结果将是:

{Super=Customer(Super-Mac),Johnny=Customer(Johnny-Puma),Mac=Customer(Super-Mac),Puma=Customer(Johnny-Puma)}
{Super=Customer(Super-Mac),Johnny=Customer(Johnny-Puma),Mac=Customer(Super-Mac),Puma=Customer(Johnny-Puma)}
{Super=Customer(Super-Mac),Johnny=Customer(Johnny-Puma),Mac=Customer(Super-Mac),Puma=Customer(Johnny-Puma)}

那么,现在我的问题是:我应该如何以Java 8有序的方式,通过
列表
,然后以某种方式将其收集为
映射
,将整个内容拆分为两个键(
first
last
),即
客户
映射两次。我不想使用任何第三方库,我不想像alt 1那样使用流之外的映射。还有其他好的选择吗


完整的代码可以用于简单的复制粘贴,以使整个程序运行。

我认为您的备选方案2和3可以重新编写,以便更加清晰:

备选方案2

Map<String, Customer> res2 = customers.stream()
    .flatMap(
        c -> Stream.of(c.first, c.last)
        .map(k -> new AbstractMap.SimpleImmutableEntry<>(k, c))
    ).collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
请注意,它们并不完全相同。如果存在重复的密钥,备选方案2将引发异常,而备选方案3将以静默方式覆盖条目

如果您希望在重复密钥的情况下覆盖条目,我个人更喜欢备选方案3。我马上就明白了它的作用。它最类似于迭代解。我希望它的性能更高,因为备选方案2必须使用所有的平面映射为每个客户进行大量分配


然而,备选方案2通过将条目的生成与其聚合分离,比备选方案3具有巨大的优势。这给了您很大的灵活性。例如,如果您想将备选方案2更改为覆盖重复键上的条目,而不是引发异常,则只需将
(a,b)->b
添加到
映射(…)
。如果您决定将匹配的条目收集到列表中,您所要做的就是将
toMap(…)
替换为
groupingBy(…)
,等等。

不仅仅是
collect()
是“首选方法”;为此使用
reduce()
是完全错误的。减少是为了价值;备选方案3所经历的扭曲违反了
reduce()
的规范。如果稍后您尝试并行运行该流,您将得到错误的答案。另一方面,如Misha所示,使用
collect()
是为了处理这种情况而设计的,无论是顺序还是并行都是安全的。@BrianGoetz谢谢你,Brian。就在你发表评论的时候,我修改了我的回答,使之更加有力。
// Alt 3: using reduce. Not so pretty
Map<String, Customer> res3 = customers.stream().reduce(
        new HashMap<>(),
        (m, p) -> {
            m.put(p.first, p);
            m.put(p.last, p);
            return m;
        }, (m1, m2) -> m2 /* <- NOT USED UNLESS PARALLEL */);
System.out.println(res1);
System.out.println(res2);
System.out.println(res3);
Map<String, Customer> res2 = customers.stream()
    .flatMap(
        c -> Stream.of(c.first, c.last)
        .map(k -> new AbstractMap.SimpleImmutableEntry<>(k, c))
    ).collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
Map<String, Customer> res3 = customers.stream()
    .collect(
        HashMap::new, 
        (m,c) -> {m.put(c.first, c); m.put(c.last, c);}, 
        HashMap::putAll
    );