Filtering/Removing 来自使用流的嵌套对象列表 Java
Filtering/Removing from a Nested List of Objects using Streams in Java
假设我们有一个 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”,如果最里面的列表中的任何元素是奇数,那么整个列表应该被删除,如果这使得第二级列表为空,那么应该在 return.
中被删除
因此,对于给定的示例,在过滤之前如果是:
[[[1,2,4],[2,4,6]], [[1,2,4]]]
过滤后应该是:
[[[2,4,6]]]
如何在 Java 中使用 Streams 执行此操作?
这是最终代码(在 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();
首先,我们通过调用 Stream#peek
遍历每个 twoDObject
,然后流式传输其列表并过滤掉每个包含奇数的 oneDObject。然后将列表保存回当前的twoDObject。
最后我们过滤掉所有空的twoDObjects。
请注意,Stream#peek
通常应仅用于调试目的,而不是改变流元素。
在这种情况下也可以替换为
List<TwoDObject> l6 = l5.stream()
.map(twoDObject -> {
...
return twoDObject;
})
...
为了实现您正在寻找的东西,我们需要混合使用功能接口的流操作和集合方法。
在下面的代码中,首先我们使用 peek 聚合操作来检查每个 TwoDbObj 实例。
然后,对于它们中的每一个,我们检查它们的 OneDbObj 列表并删除至少包含奇数的实例。
一旦 OneDbObj 列表被最里面的 Integer 列表过滤,我们只保留列表至少包含一个值(也可以写为 !isEmpty)的 TwoDbObj 实例。
List<TwoDObject> listFiltered = l5.stream()
.peek(twoObj -> twoObj.getList().removeIf(oneObj -> oneObj.getList().stream().anyMatch(i -> i % 2 != 0))) //For each TwoDbObj we get rid of the OneDbObj list whose elements contain at least an odd number
.filter(twoObj -> twoObj.getList().size() > 0) //Keeping only the TwoDbObj instances whose list contains any element
.collect(Collectors.toList());
由于您需要更新列表,在下面的解决方案中,我使用 List
的 removeIf
方法删除任何不符合必要条件的元素。因此,要使 removeIf
起作用,列表不应该是不可变的。所以用 var list = new ArrayList<>(List.of(...));
替换 var list = List.of(...)
代码
(注意:空检查也被忽略了。)
现在,这个问题可以分解成几个部分:
- 判断列表是否有奇数元素的谓词。
Predicate<OneDObject> hasOdd = obj-> obj.getList().stream().anyMatch(i -> i % 2 != 0);
- 谓词从二维列表中删除对象,该列表在其一维列表中包含奇数元素。
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();
};
- 现在将谓词应用于最终列表:
l5.removeIf(validate2d); // l5 will now contain only the 2d object having [2,4,6] list
假设我们有一个 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”,如果最里面的列表中的任何元素是奇数,那么整个列表应该被删除,如果这使得第二级列表为空,那么应该在 return.
中被删除因此,对于给定的示例,在过滤之前如果是:
[[[1,2,4],[2,4,6]], [[1,2,4]]]
过滤后应该是:
[[[2,4,6]]]
如何在 Java 中使用 Streams 执行此操作?
这是最终代码(在 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();
首先,我们通过调用 Stream#peek
遍历每个 twoDObject
,然后流式传输其列表并过滤掉每个包含奇数的 oneDObject。然后将列表保存回当前的twoDObject。
最后我们过滤掉所有空的twoDObjects。
请注意,Stream#peek
通常应仅用于调试目的,而不是改变流元素。
在这种情况下也可以替换为
List<TwoDObject> l6 = l5.stream()
.map(twoDObject -> {
...
return twoDObject;
})
...
为了实现您正在寻找的东西,我们需要混合使用功能接口的流操作和集合方法。
在下面的代码中,首先我们使用 peek 聚合操作来检查每个 TwoDbObj 实例。
然后,对于它们中的每一个,我们检查它们的 OneDbObj 列表并删除至少包含奇数的实例。
一旦 OneDbObj 列表被最里面的 Integer 列表过滤,我们只保留列表至少包含一个值(也可以写为 !isEmpty)的 TwoDbObj 实例。
List<TwoDObject> listFiltered = l5.stream()
.peek(twoObj -> twoObj.getList().removeIf(oneObj -> oneObj.getList().stream().anyMatch(i -> i % 2 != 0))) //For each TwoDbObj we get rid of the OneDbObj list whose elements contain at least an odd number
.filter(twoObj -> twoObj.getList().size() > 0) //Keeping only the TwoDbObj instances whose list contains any element
.collect(Collectors.toList());
由于您需要更新列表,在下面的解决方案中,我使用 List
的 removeIf
方法删除任何不符合必要条件的元素。因此,要使 removeIf
起作用,列表不应该是不可变的。所以用 var list = new ArrayList<>(List.of(...));
替换 var list = List.of(...)
代码
(注意:空检查也被忽略了。)
现在,这个问题可以分解成几个部分:
- 判断列表是否有奇数元素的谓词。
Predicate<OneDObject> hasOdd = obj-> obj.getList().stream().anyMatch(i -> i % 2 != 0);
- 谓词从二维列表中删除对象,该列表在其一维列表中包含奇数元素。
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();
};
- 现在将谓词应用于最终列表:
l5.removeIf(validate2d); // l5 will now contain only the 2d object having [2,4,6] list