【问题标题】:Filter list by properties of a field of the list object按列表对象字段的属性过滤列表
【发布时间】:2020-07-01 00:59:54
【问题描述】:

鉴于List<TypeA> 中的所有TypeA 实例在superTypeB 字段中设置了SubTypeB 实例,我需要从列表中过滤掉重复的TypeA 实例,其中重复表示@987654327 的值@ 和来自SubTypeBpropertyB 都匹配。有没有办法用 Java 8 流 API 做到这一点?

public class TypeA {
  private SuperTypeB superTypeB;
  public SuperTypeB getSuperTypeB(){ return superTypeB; }
}

public class SuperTypeB {
  private String propertyX;
}

public class SubTypeB extends SuperTypeB {
  private String propertyA;
  private String propertyB;
  public String getPropertyA(){ return propertyA; }
  public String getPropertyB(){ return propertyB; }
}

【问题讨论】:

  • 这能回答你的问题吗? Java 8 Distinct by property
  • 不完全。我从SubTypeB 的列表中找到了一个自定义distinctByKeys 过滤谓词函数here,它可以过滤我上面定义的重复项(propertyASubTypeB 匹配的propertyB)。我的问题是使用 TypeA 的 superTypeB 属性上的谓词函数返回过滤后的 TypeA 列表。
  • @skidalgo,为了清楚起见,假设您在列表中有三个项目,A、B、C。 A 和 B 是重复的,其中 C 是不同的。你的算法应该返回什么?
  • 你在filter之后还有一个Stream<TypeA>,所以你只需要将collect变成List<TypeA>
  • @RajanPrasad 它应该返回原始列表中每个不同对象的第一次出现。

标签: java filter java-8 java-stream


【解决方案1】:

你需要确保在filter 之前不要map 才能收集到相同的类型。使用distinctByKey utility,您可以进一步选择collectList 为:

List<TypeA> filteredTypeAs = typeAList.stream()
        .filter(distinctByKey(s -> {
            SubTypeB subTypeB = (SubTypeB) s.getSuperTypeB();
            return Arrays.asList(subTypeB.getPropertyA(), subTypeB.getPropertyB());
        }))
        .collect(Collectors.toList());

注意:这依赖于问题中所述的假设,即所有子类型都可以在没有instanceof 检查的情况下进行转换。

【讨论】:

  • 这里有两个问题。 1. 如果对于 TypeA 实例,成员 superTypeB 不属于 SubTypeB 类,则会抛出 ClassCastException, 2. 您还需要在 SubTypeB 类中提供 equals 覆盖方法。看看我的回答对你来说是否合适。
  • @RajanPrasad 对于(1),答案取决于问题所述,对于(2)也有更简单的方法来处理它。
【解决方案2】:

对于TypeASubTypeB 类型,您可以使用override equalshashCode 方法吗?

如果是,这相当简单。如果否,我将相应地编辑此答案。

class TypeA {
    private SuperTypeB superTypeB;
    public SuperTypeB getSuperTypeB(){ return superTypeB; }


        @Override
        public boolean equals( final Object o) {
            if (this == o) return true;
            if (!(o instanceof TypeA)) return false;
            TypeA typeA = (TypeA) o;
            return superTypeB.equals(typeA.superTypeB);
        }

        @Override
        public int hashCode() {
            return Objects.hash(superTypeB);
        }
    }

    class SubTypeB extends SuperTypeB {
        private String propertyA;
        private String propertyB;
        public String getPropertyA(){ return propertyA; }
        public String getPropertyB(){ return propertyB; }


        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof SubTypeB)) return false;
            SubTypeB subTypeB = (SubTypeB) o;
            return propertyA.equals(subTypeB.propertyA) &&
                propertyB.equals(subTypeB.propertyB);
        }

        @Override
        public int hashCode() {
            return Objects.hash(propertyA, propertyB);
        }

    }

在此之后,您需要做的就是:

List&lt;TypeA&gt; finalList = list.stream().distinct().collect(Collectors.toList());

您可以在流 API 中获得每个不同对象的第一次出现的唯一保证是通过流发出对象的顺序。否则,您无法控制首先遇到的元素。

【讨论】:

  • TypeA 绑定到特定的equalshashcode 可能无法扩展(如果要向此类添加另一个属性怎么办)并将其更改为进一步使用distinctByKey 会使出于同样的原因。
  • 致 OP - 当你覆盖 equals() 时,覆盖 hashCode() 也很重要,特别是当一个类需要由 distinct 使用时。看到这个 - stackoverflow.com/questions/60767490/….
  • 如果我拥有要过滤列表的类,这可能是一个更好的解决方案。在这种情况下,我没有。
  • @skidalgo 我明白了。这就是我想在答案的第一行中澄清的内容。
猜你喜欢
  • 1970-01-01
  • 2015-11-28
  • 1970-01-01
  • 1970-01-01
  • 2013-06-01
  • 1970-01-01
  • 2019-06-08
  • 2020-12-14
  • 1970-01-01
相关资源
最近更新 更多