当前位置: 首页 > 面试题库 >

Java 8流:基于不同的属性多次映射同一对象

东郭自珍
2023-03-14
问题内容

我的一位同事向我提出了一个有趣的问题,但是我找不到一个整洁漂亮的Java 8解决方案。问题是流式传输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 + ")";
    }
}

将其设置为List<Customer>

// The list of customers
List<Customer> customers = Arrays.asList(
        new Customer("Johnny", "Puma"),
        new Customer("Super", "Mac"));

备选方案1Map在“ stream”之外(或在之外forEach)使用。

// Alt 1: not pretty since the resulting map is "outside" of
// the stream. If parallel streams are used it must be
// ConcurrentHashMap
Map<String, Customer> res1 = new HashMap<>();
customers.stream().forEach(c -> {
    res1.put(c.first, c);
    res1.put(c.last, c);
});

备选方案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));

备选方案3
:到目前为止,这是我提出的“最精美”代码的另一种方案,但是它使用的三参数版本,reduce并且第三个参数在此问题中有点模糊:第三个参数用于“减少”的目的Java8函数编程中的函数。此外,reduce由于它正在变异并且并行流可能不适用于以下方法,因此似乎不太适合此问题。

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

结果将是:

{Super =客户(Super Mac),Johnny =客户(Johnny Puma),Mac = Customer(Super Mac),Puma
= Customer(Johnny Puma)}
{Super = Customer(Super Mac),Johnny = Customer(Johnny Puma), Mac
=客户(超级Mac),彪马=客户(约翰尼·彪马)}
{超级=客户(超级Mac),约翰尼=客户(约翰尼·彪马),Mac =客户(超级Mac),彪马=客户(约翰尼·彪马)}

所以,现在我的问题是:我应该如何以Java 8有序的方式流经List<Customer>,然后以某种方式收集它,Map<String, Customer>将整个事物拆分为两个键(firstAND last),即Customer映射两次。我不想使用任何第三方库,也不想像alt
1一样在流之外使用地图。还有其他不错的选择吗?

完整的代码可以在hastebin上找到,以进行简单的复制粘贴以使整个过程正常运行。


问题答案:

我认为您的备选方案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));

备选方案3reduce通过更改HashMap来滥用代码。要进行可变还原,请使用collect

Map<String, Customer> res3 = customers.stream()
    .collect(
        HashMap::new, 
        (m,c) -> {m.put(c.first, c); m.put(c.last, c);}, 
        HashMap::putAll
    );

请注意,这些不相同。如果键重复,则选项2将引发异常,而选项3将静默覆盖条目。

如果您想要在重复键的情况下覆盖条目,我个人更喜欢替代方案3。它与迭代解决方案最相似。我希望它的性能更高,因为备选方案2必须在所有扁平化的情况下为每位客户分配大量资金。

但是,通过将条目的产生与聚合分开,替代方案2与替代方案3相比具有巨大优势。这给您很大的灵活性。例如,如果要更改替代方法2以覆盖重复键上的条目而不是引发异常,则只需将添加(a,b) -> b到即可toMap(...)。如果你决定要收集匹配条目到列表中,您所要做的就是更换toMap(...)groupingBy(...)等。



 类似资料:
  • 我有一个类具有以下签名: 我想将映射到在单个流中同时包含stringA和stringB的 或: 当然他们都不编译,但你明白了。 编辑: 示例:

  • 你好,我的问题与Java8非常相似:流映射同一个流中的两个属性,但由于某些原因,我无法让代码正常工作。 所以就像这个问题一样,假设我有一个班级 所以我想从列表中组合这些

  • 问题内容: 我正在使用ObjectMapper(https://github.com/Hearst- DD/ObjectMapper )将JSON映射到Swift对象。 说我有这个JSON结构: 我有以下Swift对象: 如何使用JSON对象中的键区分映射和映射?非常感谢! 问题答案: 细节 Xcode 10.2.1(10E1001),Swift 5 json档案 ObjectMapper示例 检

  • 对特定的持久化类,映射多次是允许的。这种情形下,你必须指定 entity name 来区别不同映射实体的对象实例。(默认情况下,实体名字和类名是相同的。) Hibernate 在操作持久化对象、编写查询条件,或者把关联映射到指定实体时,允许你指定这个 entity name(实体名字)。 <class name="Contract" table="Contracts" entity

  • 我最近开始使用 DynamoDB,我看到要使用 DynamoDB 映射器框架,我需要先知道属性的名称,然后再将其绑定到类变量。如果我必须实现几个动态变化的属性,但仍然希望能够在扫描期间对它们设置条件,该怎么办?我尝试在类中绑定一个哈希映射(假设在变量“attrMap”下),并编写我自己的编组器来设置它。但是,我注意到它在Dynamo表中保存为一个具有复杂值的属性“attrMap”,这不允许我在扫描

  • 我已经尝试了表2的hibernate映射: 上面不起作用。在获取记录时,它试图从表2中获取表1中根本不存在的LOB代码