我正在尝试使用流过滤具有两个不同谓词的POJO列表。
public class Toy {
public boolean isRed();
public boolean isBlue();
public boolean isGreen();
}
public class RedBlueExtravaganza {
public RedBlueExtravaganza(RedToy rt, BlueToy bt) {
//construct
}
}
// Wrappers around Toy with more verbose names
public class RedToy extends Toy { }
public class BlueToy extends Toy { }
public class GreenToy extends Toy { }
[基本上,我想要玩具对象列表中的第一个红色和蓝色玩具。
List<Toy> toyList = Arrays.asList(greenToy1, redToy1, greenToy2, redToy2, blueToy1);
我想编写一个执行以下操作的流:
RedBlueExtravaganza firstRedBlueList = toyList.stream()
// get first red but keep rest of list
// get first blue but keep rest of list
// discard rest of list
// map to a Tuple (or something) to distinguish the one red and one blue toy
// map to RedBlueExtravaganza
.findFirst()
.get();
log.info(firstRedBlueList); // now contains redToy1, blueToy1
感谢您的帮助!
这是一个解决方案,它只遍历您的列表一次,最后给您第一个红色和蓝色玩具。我们可以过滤掉所有其他不相关的颜色,然后创建一个映射,其键为玩具是否为红色,值是否为符合给定条件的第一个玩具。这是它的外观。
Map<Boolean, Toy> firstRedAndBlueToysMap = toyList.stream()
.filter(t -> t.isBlue() || t.isRed())
.collect(Collectors.toMap(Toy::isRed, t -> t, (a, b) -> a));
Toy firstRedToy = firstRedAndBlueToysMap.get(true);
Toy firstBlueToy = firstRedAndBlueToysMap.get(false);
这是解决问题的一种方法。
RedBlueExtravaganza firstRedAndBlueToyPair = toyList.stream()
.filter(t -> t.isBlue() || t.isRed())
.collect(Collectors.collectingAndThen(Collectors.toMap(Toy::isRed,
t -> t, (a, b) -> a),
m -> new RedBlueExtravaganza(m.get(true), m.get(false))));
P.S。为此,您需要在您的RedBlueExtravaganza
类中使用以下构造函数,与上面提供的相反。
public RedBlueExtravaganza(Toy rt, Toy bt) {
if (!(rt instanceof RedToy) || !(bt instanceof BlueToy))
throw new IllegalArgumentException();
// remainder omitted.
}
首先想到的一种方法是使用2个流分别查找第一个红色和蓝色玩具对象。然后使用Stream.concat()
将它们合并为一个流,以便为此添加更多操作。
代码段
RedBlueExtravaganza firstRedBlueList = Stream
.concat(
toyList.stream().filter(t -> t.isRed())
.findFirst()
.map(Collections::singletonList)
.orElseGet(Collections::emptyList)
.stream(),
toyList.stream().filter(t -> t.isBlue())
.findFirst()
.map(Collections::singletonList)
.orElseGet(Collections::emptyList)
.stream())
.map(x -> {
RedToy rt = new RedToy();
BlueToy bt = new BlueToy();
...
return new RedBlueExtravaganza(rt, bt);})
.findFirst()
.get();
您可以创建一个接受List<Toy>
的util方法并过滤Predicate
以获得结果
public Toy getMatchedElement(List<Troy> list, Predicated<Troy> filter) {
return list.stream().filter(filter).findFirst().orElse(//default value);
}
您可以通过过滤器来获取第一个红色元素
getMatchedElement(list, Troy::isRed);
或获取蓝色元素
getMatchedElement(list, Troy::isBlue);
我喜欢this answers,类似的解决方案可以通过reduce和List作为“ Tuple(或其他)”]
List<Toy> reduceToList = toyList.stream()
.filter(t -> t.isBlue() || t.isRed())
.map(t -> Arrays.asList(t, t))
.reduce(Arrays.asList(null, null), (a, c) -> a.get(0) == null && c.get(0).isRed() ?
Arrays.asList(c.get(0), a.get(1)) : (a.get(1) == null && c.get(1).isBlue() ?
Arrays.asList(a.get(0), c.get(1)) : a)
);
如果两个值都不为null,则可以映射到RedBlueExtravaganza