【问题标题】:Merging two List of objects in java 8在java 8中合并两个对象列表
【发布时间】:2015-06-16 11:51:31
【问题描述】:

我有一个 Java 类 Parent 有 20 个属性 (attrib1, attrib2 .. attrib20) 及其对应的 getter 和 setter。我还有两个 Parent 对象列表:list1list2

现在我想合并两个列表并避免基于attrib1attrib2 的重复对象。

使用 Java 8:

List<Parent> result = Stream.concat(list1.stream(), list2.stream())
                .distinct()
                .collect(Collectors.toList());   

但是我必须在哪个地方指定属性?我应该重写hashCodeequals 方法吗?

【问题讨论】:

标签: java java-8 java-stream


【解决方案1】:

如果你想实现equalshashCode,可以在Parent 中实现。在该类中添加方法,如

    @Override
    public int hashCode() {
        return Objects.hash(getAttrib1(), getAttrib2(), getAttrib3(),
            // …
                            getAttrib19(), getAttrib20());
    }

    @Override
    public boolean equals(Object obj) {
        if(this==obj) return true;
        if(!(obj instanceof Parent)) return false;
        Parent p=(Parent) obj;
        return Objects.equals(getAttrib1(), p.getAttrib1())
            && Objects.equals(getAttrib2(), p.getAttrib2())
            && Objects.equals(getAttrib3(), p.getAttrib3())
            // …
            && Objects.equals(getAttrib19(), p.getAttrib19())
            && Objects.equals(getAttrib20(), p.getAttrib20());
    }

如果您这样做了,在 Stream&lt;Parent&gt; 上调用的 distinct() 将自动执行正确的操作。


如果您不想(或不能)更改类 Parent,则没有相等的委托机制,但您可以诉诸 ordering,因为它具有委托机制:

Comparator<Parent> c=Comparator.comparing(Parent::getAttrib1)
        .thenComparing(Parent::getAttrib2)
        .thenComparing(Parent::getAttrib3)
        // …
        .thenComparing(Parent::getAttrib19)
        .thenComparing(Parent::getAttrib20);

这定义了基于属性的顺序。它要求属性本身的类型具有可比性。如果你有这样的定义,你可以用它来实现一个distinct()的等价物,基于那个Comparator

List<Parent> result = Stream.concat(list1.stream(), list2.stream())
        .filter(new TreeSet<>(c)::add)
        .collect(Collectors.toList());

还有一个线程安全的变体,以防您想将它与并行流一起使用:

List<Parent> result = Stream.concat(list1.stream(), list2.stream())
        .filter(new ConcurrentSkipListSet<>(c)::add)
        .collect(Collectors.toList());

【讨论】:

  • 这不会为新列表中的每个元素创建一个 TreeSet/ConcurrentSkipListSet 的新实例吗?
  • @VivekKothari 不,这是expression::name 形式的方法引用的基本属性,即首先评估表达式,然后由功能接口实例捕获结果,该实例只会调用该对象上的方法。参见,例如“What is the equivalent lambda expression for System.out::println?”或this Q&Athat Q&A
【解决方案2】:

例如:

public class Parent {

    public int no;
    public String name;

    @Override
    public int hashCode() {
        return (no << 4) ^ name.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Parent))
            return false;
        Parent o = (Parent)obj;
        return this.no == o.no && this.name.equals(o.name);
    }
}

【讨论】:

    【解决方案3】:

    覆盖Parent 类中的equalshashCode 方法以避免列表中的重复。这将为您提供您想要的确切结果。

    【讨论】:

    【解决方案4】:

    如果你想覆盖.equals(…).hashCode(),你需要在Parent 类上这样做。请注意,这可能会导致 Parent 的其他用途失败。 Alexis C. 的链接解决方案更加保守。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-26
      • 2018-06-05
      • 1970-01-01
      • 2021-09-04
      • 1970-01-01
      相关资源
      最近更新 更多