java - 如何使用 Stream API 合并然后区分集合?

标签 java java-8 java-stream

<分区>

让我们用我的对象作为前缀,等于实现不是我需要过滤的方式,所以 distinct 本身不起作用。

class MyObject {
  String foo;
  MyObject( String foo ) {
    this.foo = foo;
  }
  public String getFoo() { return foo; }
}


Collection<MyObject> listA = Arrays.asList("a", "b", "c").stream().map(MyObject::new)
        .collect(Collectors.toList());

Collection<MyObject> listB = Arrays.asList("b", "d").stream().map(MyObject::new)
        .collect(Collectors.toList());


// magic

如何合并列表并删除重复项,以便生成的列表应该是包含“a”、“b”、“c”、“d”的 MyObjects?

注意:这是对我们实际需要去重的方法的简化,这些方法实际上是 hibernate 加载的实体的复杂 DTO,但这个例子应该充分展示了目标。

最佳答案

JDK 开发人员讨论了此类功能(请参阅 JDK-8072723),并且可能包含在 Java-9 中(但不保证)。 StreamEx我开发的库已经有这样的功能,所以你可以使用它:

List<MyObject> distinct = StreamEx.of(listA).append(listB)
                                  .distinct(MyObject::getFoo).toList();

StreamEx类是增强的 Stream它与 JDK Stream 完全兼容,但有许多额外的操作,包括 distinct(Function) 它允许您为不同的操作指定 key 提取器。在内部,它与@fge 提出的解决方案非常相似。

您还可以考虑编写自定义收集器,它将获取不同的对象并将它们存储到列表中:

public static <T> Collector<T, ?, List<T>> distinctBy(Function<? super T, ?> mapper) {
    return Collector.<T, Map<Object, T>, List<T>> of(LinkedHashMap::new,
        (map, t) -> map.putIfAbsent(mapper.apply(t), t), (m1, m2) -> {
            for(Entry<Object, T> e : m2.entrySet()) {
                m1.putIfAbsent(e.getKey(), e.getValue());
            }
            return m1;
        }, map -> new ArrayList<>(map.values()));
}

此收集器中间将结果收集到 Map<Key, Element> 中其中Key是提取出来的Key,Element是对应的流元素。为了确保在所有重复元素中准确地保留第一个出现的元素,LinkedHashMap用来。最后你只需要拿 values()这张 map 并将它们转储到列表中。所以现在你可以写:

List<MyObject> distinct = Stream.concat(listA.stream(), listB.stream())
                                .collect(distinctBy(MyObject::getFoo));

如果您不关心生成的集合是否为列表,您甚至可以删除 new ArrayList<>()步骤(仅使用 Map::values 作为终结者)。如果您不关心顺序,还可以进行更多简化:

public static <T> Collector<T, ?, Collection<T>> distinctBy(Function<? super T, ?> mapper) {
    return Collector.<T, Map<Object, T>, Collection<T>> of(HashMap::new,
            (map, t) -> map.put(mapper.apply(t), t), 
            (m1, m2) -> { m1.putAll(m2); return m1; }, 
            Map::values);
}

这样的收集器(保留顺序并返回 List )也是 available在 StreamEx 库中。

关于java - 如何使用 Stream API 合并然后区分集合?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32854461/

相关文章:

java - 如何使用 Java 8 流过滤列表并从值数组开始

java - 更简单的方法来反转由静态方法(功能接口(interface))内联创建的比较器?

java - Swing 布局想法

java - 如何向 Vaadin 项目添加新 UI?

Java 8 兼容性问题 : How to convert Object array to Subtype List in Java 8?

java - 定义 lambda 表达式时使用泛型类型参数 <T>

java - jackson YAML : mapping a regex Pattern with flags

java - 在几个时间间隔后将数据发送到 servlet

lambda - 如何在 Java 功能接口(interface)中使用 andThen 或类似方法包装函数

java - 流和功能接口(interface): throw exception inside stream and aggregate them