Отфильтруйте первый элемент, который соответствует предикату, с несколькими проверками - PullRequest
4 голосов
/ 30 октября 2019

Я пытаюсь отфильтровать список 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

Спасибо за помощь!

Ответы [ 3 ]

3 голосов
/ 30 октября 2019

Вот решение, которое обходит ваш список только один раз, давая вам первые красные и синие игрушки в конце. Мы можем отфильтровать все другие нежелательные цвета и затем создать карту, ключом которой является то, является ли игрушка красной, и значение - первая игрушка, соответствующая заданным критериям. Вот как это выглядит.

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 Чтобы это работало, в вашем * 1007 должен быть следующий конструктор* класс, противоположный тому, который вы указали выше.

public RedBlueExtravaganza(Toy rt, Toy bt) {
    if (!(rt instanceof RedToy) || !(bt instanceof BlueToy))
        throw new IllegalArgumentException();

    // remainder omitted.
}
2 голосов
/ 30 октября 2019

Мне нравится это ответы , аналогичное решение может быть с помощью 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

0 голосов
/ 30 октября 2019

Первый способ, который приходит мне в голову, - это использовать 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();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...