为了账号安全,请及时绑定邮箱和手机立即绑定

通过多次检查过滤与谓词匹配的第一个元素

通过多次检查过滤与谓词匹配的第一个元素

泛舟湖上清波郎朗 2024-01-05 16:54:49
我正在尝试使用流过滤具有两个不同谓词的 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 namespublic 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谢谢您的帮助!
查看完整描述

3 回答

?
呼唤远方

TA贡献1856条经验 获得超11个赞

这是一个仅遍历列表一次的解决方案,在最后为您提供第一个红色和蓝色玩具。我们可以过滤掉所有其他不相关的颜色,然后创建一个映射,其键是玩具是否是红色,值是符合给定条件的第一个玩具。它看起来是这样的。


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))));

PS 为此,您需要在RedBlueExtravaganza类中拥有以下构造函数,与上面提供的构造函数相反。


public RedBlueExtravaganza(Toy rt, Toy bt) {

    if (!(rt instanceof RedToy) || !(bt instanceof BlueToy))

        throw new IllegalArgumentException();


    // remainder omitted.

}


查看完整回答
反对 回复 2024-01-05
?
慕标5832272

TA贡献1966条经验 获得超4个赞

类似的解决方案可以是减少并列出为“元组(或其他)”

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)

      );

如果两个值都不为空,那么您可以映射到RedBlueExtravaganza


查看完整回答
反对 回复 2024-01-05
?
隔江千里

TA贡献1906条经验 获得超10个赞

我首先想到的一种方法是使用 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();


查看完整回答
反对 回复 2024-01-05
  • 3 回答
  • 0 关注
  • 108 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信