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

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

我正在尝试使用流过滤具有两个不同谓词的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

感谢您的帮助!

java java-stream
4个回答
1
投票

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

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.
}

0
投票

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

0
投票

您可以创建一个接受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);

0
投票

我喜欢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

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