提问人:CurvedHalo 提问时间:3/31/2022 最后编辑:CurvedHalo 更新时间:4/4/2022 访问量:942
使用流筛选列表和嵌套列表
Filtering lists and nested lists using streams
问:
我必须根据属性的值筛选列表。我还必须根据嵌套列表的一个属性来过滤它,同样地,对于另一个嵌套列表。我想知道这在流中怎么可能。
例:
- 我想过滤 Foo 的列表,只保留 Foo.type = “fooType” 的那些。
- 在这些保留的 Foo 中,我希望过滤 Bar.type = “barType” 上的 Bar 列表,仅保留满足给定条件的 Bar。
- 然后,我想过滤 NestedAttribute.id = “attributeID” 上的 NestedAttribute 列表,只保留与此条件匹配的那些。
- 在这些保留的 Foo 中,我希望过滤 Bar.type = “barType” 上的 Bar 列表,仅保留满足给定条件的 Bar。
我想从这里返回 foo 的列表。
void test() {
List<Foo> listOfFoos;
for(Foo foo : listOfFoos) {
if(foo.getType().equalsIgnoreCase("fooType")) {
// If foo matches condition, retain it
for(Bar bar : foo.getBars()) {
if(bar.getType().equalsIgnoreCase("barType")) {
// If Bar matches condition, retain this Bar
for(NestedAttribute attribute : bar.getNestedAttributes()) {
if(attribute.getId().equalsIgnoreCase("attributeID")) {
// retain this attribute and return it.
}
}
} else {
// remove bar from the list
foo.getBars().remove(bar);
}
}
}else {
// remove Foo from list
listOfFoos.remove(foo);
}
}
}
@Getter
@Setter
class Foo {
String type;
List<Bar> bars;
}
@Getter
@Setter
class Bar {
String type;
List<NestedAttribute> nestedAttributes;
}
@Getter
@Setter
class NestedAttribute {
String id;
}
我试过这个:
listOfFoos = listOfFoos.stream()
.filter(foo -> foo.getType().equalsIgnoreCase("fooType"))
.flatMap(foo -> foo.getBars().stream()
.filter(bar -> bar.getType().equalsIgnoreCase("barType"))
.flatMap(bar -> bar.getNestedAttributes().stream()
.filter(nested -> nested.getId().equalsIgnoreCase("attributeID"))
)
).collect(Collectors.toList());
答:
1赞
Ovidijus Parsiunas
3/31/2022
#1
您可以使用流过滤器 lambda 表达式执行此操作,但不幸的是,生成的内聚力不会很好:
listOfFoos.stream()
.filter(foo ->
(foo.getType().equalsIgnoreCase("fooType") && (foo.getBars().stream()
.filter((bar -> (bar.getType().equalsIgnoreCase("barType") && (bar.getNestedAttributes().stream()
.filter(nestedAttribute -> nestedAttribute.getId().equalsIgnoreCase("attributeID"))
).count() > 0)))
).count() > 0))
.collect(Collectors.toList());
1赞
Adriaan Koster
3/31/2022
#2
我假设你想要所有的“fooType”foos,只有“barType”条和“attributeID”嵌套的Attibutes。
然后是这样的:
List<Foo> selected = listOfFoos.stream()
// keep the "footType" foos
.filter(foo -> foo.getType().equalsIgnoreCase("fooType"))
// map each foo to itself
.map(foo -> {
// ... but sneakily remove the non-"barType" bars
foo.getBars().removeIf(bar -> !bar.getType().equalsIgnoreCase("barType"))
return foo;
}
// map each foo to itself again
.map(foo -> {
// iterate over the bars
foo.getBars().forEach(bar ->
// remove the non-"attributeID" nested attributes
bar.getNestedAttributes().removeIf(nested -> !nested.getId().equalsIgnoreCase("attributeID"))
);
return foo;
}
.collect(Collectors.toList());
请注意,这实际上是在修改嵌套集合,而不仅仅是创建流。要获取筛选的嵌套集合,需要像这样执行此操作,或者创建新的嵌套集合。
1赞
dani-vta
3/31/2022
#3
这就是你要找的
public static List<Foo> filterList(List<Foo> list, String fooType, String barType, String attrID) {
return list.stream()
.filter(foo -> foo.getType().equalsIgnoreCase(fooType))
.peek(foo -> foo.getBars().removeIf(bar -> !bar.getType().equalsIgnoreCase(barType)))
.peek(foo -> foo.getBars().forEach(bar -> bar.getNestedAttributes().removeIf(attr -> !attr.getId().equalsIgnoreCase(attrID))))
.collect(Collectors.toList());
}
编辑:添加了带有 toString 的类实现以进行测试打印
public class Main {
public static void main(String[] args) {
ArrayList barListAttrs = new ArrayList();
barListAttrs.add(new NestedAttribute("testAttr1"));
barListAttrs.add(new NestedAttribute("id"));
barListAttrs.add(new NestedAttribute("testAttr2"));
ArrayList fooListBars = new ArrayList();
fooListBars.add(new Bar("bar", barListAttrs));
fooListBars.add(new Bar("testBar1", new ArrayList<>()));
List<Foo> listFoo = new ArrayList<>();
listFoo.add(new Foo("testFoo1", new ArrayList<>()));
listFoo.add(new Foo("foo", fooListBars));
for (Foo f : listFoo) {
System.out.println(f);
}
List<Foo> list2 = filterList(listFoo, "foo", "bar", "id");
System.out.println("\n\n---------------- RESULT ----------------\n");
for (Foo f : list2) {
System.out.println(f);
}
}
}
class Foo {
String type;
List<Bar> bars;
public Foo(String type, List<Bar> bars) {
this.type = type;
this.bars = bars;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public List<Bar> getBars() {
return bars;
}
public void setBars(List<Bar> bars) {
this.bars = bars;
}
@Override
public String toString() {
StringBuilder str = new StringBuilder(type);
str.append(" [");
for (Bar b : bars) {
str.append(b.toString());
str.append(" ");
}
str.append("]");
return str.toString();
}
}
class Bar {
String type;
List<NestedAttribute> nestedAttributes;
public Bar(String type, List<NestedAttribute> nestedAttributes) {
this.type = type;
this.nestedAttributes = nestedAttributes;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public List<NestedAttribute> getNestedAttributes() {
return nestedAttributes;
}
public void setNestedAttributes(List<NestedAttribute> nestedAttributes) {
this.nestedAttributes = nestedAttributes;
}
@Override
public String toString() {
StringBuilder str = new StringBuilder(type);
str.append(" [");
for (NestedAttribute na : nestedAttributes) {
str.append(na.toString());
str.append(" ");
}
str.append("]");
return str.toString();
}
}
class NestedAttribute {
String id;
public NestedAttribute(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public String toString() {
return id;
}
}
1赞
WJS
3/31/2022
#4
我确信你可以用流来做到这一点,但我不认为它非常适合这样做。问题在于,流和地图将现有元素替换为新元素,可能是不同类型的元素。
但是,有必要保持对以前构造的类型的访问以构建层次结构。 将是一种可能性,但它可能会变得杂乱无章(比下面更是如此)。mapMulti
下面创建一个不删除任何内容的新层次结构(在随机访问列表中删除可能会很昂贵,因为需要线性搜索或重复复制值),并添加包含所需类型的实例。在每个条件中,都会创建一个新实例。届时,上一个列表将更新以反映刚刚创建的实例。
在生成一些可变数据后,这似乎在我理解目标时起作用。
static List<Foo> test(List<Foo> listOfFoos) {
List<Foo> newFooList = new ArrayList<>();
for (Foo foo : listOfFoos) {
if (foo.getType().equalsIgnoreCase("fooType")) {
Foo newFoo = new Foo(foo.getType(), new ArrayList<>());
newFooList.add(newFoo);
for (Bar bar : foo.getBars()) {
if (bar.getType().equalsIgnoreCase("barType")) {
Bar newBar = new Bar(bar.getType(), new ArrayList<>());
newFoo.getBars.add(newBar);
for (NestedAttribute attribute : bar
.getNestedAttributes()) {
if (attribute.getId().equalsIgnoreCase(
"attributeID")) {
newBar.getNestedAttributes().add(attribute);
}
}
}
}
}
}
return newFooList;
}
1赞
Ravi Gupta
4/1/2022
#5
您可以尝试此选项。这不是流利的陈述,而是三个流利的陈述。
Function<Bar, List<NestedAttribute>> filterAttributes
= bar -> bar.getNestedAttributes()
.stream()
.filter(a -> "attributeId".equals(a.getId()))
.collect(Collectors.toList());
Function<Foo, List<Bar>> filterBarsAndAttributes
= foo -> foo.getBars()
.stream()
.filter(b -> "barType".equals(b.getType()))
.peek(b -> b.setNestedAttributes(filterAttributes.apply(b)))
.collect(Collectors.toList());
listOfFoos.stream()
.forEach(f -> f.setBars(filterBarsAndAttributes.apply(f)));
上一个:如何实现嵌套列表迭代器
评论
listOfFoos.stream.filter(...)
filter