【问题标题】:Java 8 filter list of pojos based on nested object attribute基于嵌套对象属性的 Java 8 过滤器列表
【发布时间】:2017-02-20 20:34:08
【问题描述】:

我有一个 Thingy pojo 的列表,例如:

public class Thingy {
    private DifferentThingy nestedThingy;
    public DifferentThingy getNestedThingy() {
        return this.nestedThingy;
    }
}

...

public class DifferentThingy {
    private String attr;
    public String getAttr() {
        return this.attr;
    }
}

我要过滤一个

List<Thingy>

是唯一的基于

attr

Thingy 的

DifferentThingy

这是我迄今为止尝试过的:

private List<Thingy> getUniqueBasedOnDifferentThingyAttr(List<Thingy> originalList) {
    List<Thingy> uniqueItems = new ArrayList<Thingy>();
    Set<String> encounteredNestedDiffThingyAttrs= new HashSet<String>();
    for (Thingy t: originalList) {
        String nestedDiffThingyAttr = t.getNestedThingy().getAttr();
        if(!encounteredNestedDiffThingyAttrs.contains(nestedDiffThingyAttr)) {
            encounteredNestedDiffThingyAttrs.add(nestedDiffThingyAttr);
            uniqueItems.add(t);
        }
    }
    return uniqueItems;
}

我想为最终检索用于确定唯一性的属性的两个 getter 使用 Java 8 流和 lambda,但我不确定如何。当用于比较的属性位于 pojo 的顶层时,我知道该怎么做,但当属性嵌套在另一个对象中时,我不知道。

【问题讨论】:

标签: java lambda java-8 java-stream


【解决方案1】:

你可以这样做:

originalList.stream()
    .collect(Collectors.toMap(thing -> thing.getNestedThingy().getAttr(), p -> p, (p, q) -> p))
    .values();

不确定是否是最佳方式,我在 Android 上,所以不要在日常工作中使用流。

UPD

对于无法编译的人,我的测试文件有full code

【讨论】:

  • 以上有人测试过吗?我有一个java.lang.ClassCastException: java.util.HashMap$Values cannot be cast to java.util.List
  • 我的解决方案大致相同: public static Collection getUniqueBasedOnDifferentThingyAttr(List originalList) { return originalList.stream() .collect(Collectors.toMap(t -> t.getNestedThingy( ).getAttr(), t -> t)) .values(); }
  • @Andreas,我认为这取决于发生碰撞时他们想要哪一个。
  • @GrzegorzGórkiewicz 是的,我是用想法写的,它是 100% 工作代码
  • @JellyRaptor 我添加了指向我的测试类的完整代码的链接。
【解决方案2】:

如何使用javaslang 使其更加明确

javaslang.collection.List.ofAll(thingies)
  .distinctBy(t -> t.getNestedThingy().getAttr());

Javaslang 核心是 Java 8+ 的函数库。它有助于减少代码量并增加健壮性。函数式编程的第一步是开始思考不可变的值。 Javaslang 提供了不可变集合以及对这些值进行操作所需的函数和控制结构。结果很漂亮,而且很有效。

【讨论】:

    【解决方案3】:

    我保留了一组独特的嵌套事物的属性... 然后我尝试向它添加一个元素。 当集合“增长”时,意味着遇到了一个独特的元素。 当设置大小没有改变时,这意味着这个Thingy 有一个NestedThingy 带有一个已经遇到的属性。 再加上一个简单的测试:

    public class Thingies {
    
        public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
            final Set<Object> seen = Collections.synchronizedSet(new HashSet<>());
            return t -> seen.add(keyExtractor.apply(t));
        }
    
        private static List<Thingy> getUniqueBasedOnDifferentThingyAttr(List<Thingy> originalList) {
            return originalList.stream()
                    .filter(distinctByKey(thingy -> thingy.getNestedThingy().getAttr()))
                    .collect(toList());
        }
    
        public static void main(String[] args) {
            List<Thingy> originalList = new ArrayList<>();
            originalList.add(new Thingy(new DifferentThingy("first")));
            originalList.add(new Thingy(new DifferentThingy("first")));
            originalList.add(new Thingy(new DifferentThingy("second")));
            System.out.println(getUniqueBasedOnDifferentThingyAttr(originalList));
        }
    }
    

    输出:

    [first, second]
    

    帮助 OP 的原始答案(有点冗长,不适合并行流)包含这样的方法:

    private static List<Thingy> getUniqueBasedOnDifferentThingyAttr(List<Thingy> originalList) {
        final Set<String> uniqueAttributes = new HashSet<>(originalList.size());
        return originalList.stream()
                .filter(thingy -> {
                    int initialSize = uniqueAttributes.size();
                    uniqueAttributes.add(thingy.getNestedThingy().getAttr());
                    return initialSize != uniqueAttributes.size();
                }).collect(toList());
    }
    

    【讨论】:

    • 如果我将 toList() 更改为 Collectors.toList(),这将有效
    • 那就去吧 ;) 我可以尝试使用 distinctBy 方法实现使其更短。
    • 从内部流链更改外部值看起来对我来说很重要。
    • @Divers,它包含在一种方法中。重新分配变量是一种不好的做法(这里不可能,因为final),利用它的可变性(这里:add 方法调用)是一种常见的做法,尤其是在void 方法中。说了这么多,我还是喜欢你的代码……它返回一个Collection&lt;Thingy&gt; 的实例而不是List&lt;Thingy&gt;,这是它唯一的缺点(很容易修复)。
    • 感谢大家的建设性批评,我将编辑我的答案。
    【解决方案4】:

    您可以使用TreeSet,它的比较器可以到达子对象的属性:

    private static List<Thingy> getUniqueBasedOnDifferentThingyAttr(List<Thingy> originalList) {
    
        Set<Thingy> uniqueSet = new TreeSet<Thingy>(Comparator.comparing(thingy -> thingy.getNestedThingy().getAttr()));
    
        return originalList.stream()
                .filter(uniqueSet::add)
                .collect(Collectors.toList());
    }
    

    uniqueSet::add 是一个足够的唯一性过滤器,因为它仅在项目不在集合中时才返回 true。

    【讨论】:

    • 使用长度为 5 的原始列表和 3 个“唯一”项目,此解决方案平均比 @GrzegorzGórkiewicz 最初接受的答案快 6 倍(11.8 毫秒到 66 毫秒)。我也喜欢使用 Comparator。
    • Imho Hank D 的答案应该是被接受的。它很简单,而且很漂亮。
    【解决方案5】:

    StreamEx:

    StreamEx.of(originalList).distinct(e -> e.getNestedThingy().getAttr()).toList();
    

    AbacusUtil

    Stream.of(originalList).distinct(e -> e.getNestedThingy().getAttr()).toList();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-20
      • 1970-01-01
      • 1970-01-01
      • 2020-06-17
      • 2019-07-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多