在 Java 中使用流从嵌套的对象列表中过滤/删除

问题描述 投票:0回答:3

假设我们有一个 3 维对象列表:

class OneDObject {
  int id;
  List<Integer> list;
  OneDObject(int id, List<Integer>list) { /* Constructor */ }
  // Getters and Setters
}

class TwoDObject {
  int id;
  List<OneDObject> list;
  TwoDObject(int id, List<OneDObject> list) { /* Constructor */ }
  // Getters and Setters
}

var l1 = List.of(1,2,4);
var l2 = List.of(2,4,6);
var obj1d1 = new OneDObject(1, l1);
var obj1d2 = new OneDObject(2, l2);
var l3 = List.of(obj1d1, obj1d2);
var l4 = List.of(obj1d1);
var obj2d1 = new TwoDObject(3, l3);
var obj2d2 = new TwoDObject(4, l4);
var l5 = List.of(obj2d1, obj2d2);   // 3-d list

假设我想过滤“l5”,这样如果最里面的列表中的任何元素是奇数,则应删除整个列表,如果这使得第二级列表为空,则应将其删除.

因此,对于给定的示例,在过滤之前:

[[[1,2,4],[2,4,6]], [[1,2,4]]]

过滤后应该是:

[[[2,4,6]]]

如何使用 Java 中的流来做到这一点?

java list java-stream java-11
3个回答
3
投票

由于您需要更新列表,因此在下面的解决方案中,我使用

removeIf
List
方法来删除任何不符合必要条件的元素。因此,为了让
removeIf
发挥作用,列表不应该是一成不变的。因此将
var list = List.of(...)
代码替换为
var list = new ArrayList<>(List.of(...));
(注意:空检查也被忽略。)

现在,这个问题可以分解为几个部分:

  1. 判断列表是否有奇数元素。
Predicate<OneDObject> hasOdd = obj-> obj.getList().stream().anyMatch(i -> i % 2 != 0);
  1. 谓词从 2d 列表中删除对象,该对象的 1d 列表中包含奇数元素。
Predicate<TwoDObject> validate2d = obj -> {
    // remove any 1d list that has atleast one odd number.
    obj.getList().removeIf(hasOdd);
    // check if there are any valid 1d lists
    return obj.getList().isEmpty();
};
  1. 现在将谓词应用于最终列表:
l5.removeIf(validate2d); // l5 will now contain only the 2d object having [2,4,6] list

1
投票

这是最终代码(Java 语言,但我认为它应该几乎可以与 Kotlin 互换)

List<TwoDObject> l6 = l5.stream()
                .peek(twoDObject -> {
                    List<OneDObject> filteredOneDObjectList = twoDObject.getList()
                            .stream()
                            .filter(oneDObject -> oneDObject.getList()
                                    .stream()
                                    .noneMatch(i -> i % 2 == 1))
                            .toList();

                    twoDObject.setList(filteredOneDObjectList);
                })
                .filter(twoDObject -> twoDObject.getList().size() > 0)
                .toList();

首先,我们通过调用

twoDObject
来遍历每个
Stream#peek
,然后流式传输其列表并过滤掉每个包含奇数的 oneDObject。然后列表被保存回当前的twoDObject。

最后我们过滤掉所有空的twoDObject。


请注意,

Stream#peek
通常仅用于调试目的,而不是改变流元素。
在这种情况下也可以替换为

List<TwoDObject> l6 = l5.stream()
                .map(twoDObject -> {
                    ...

                    return twoDObject;
                })
                ...

0
投票

如果您实际上并不关心改变列表并保留起始

TwoDObject
对象,并且创建新对象是可以接受的,则以下命令将返回所需的结果:

List<TwoDObject> filtered = l5.stream()
        .map(do2 -> new TwoDObject(do2.getId(), do2.getList().stream().filter(
                do1 -> do1.getList().stream().noneMatch(e -> e % 2 == 1)).toList()))
        .filter(do2 -> !do2.getList().isEmpty())
        .toList();

这会为列表中的每个元素创建新的

TwoDObject
复制值,但其
list
值会被过滤为仅包含不包含奇数的
OneDObject
值。 然后,它会过滤掉列表中剩余零个项目的任何新
TwoDObject
值(即每个
TwoDObject
,其中所有
OneDObject
元素都已被上一步过滤掉)。

© www.soinside.com 2019 - 2024. All rights reserved.