Может ли Java 8 Streams работать с элементом в коллекции, а затем удалить его?


как и почти все, я все еще изучаю тонкости (и люблю их) нового API Java 8 Streams. У меня есть вопрос относительно использования потоков. Я приведу упрощенный пример.

Java Streams позволяет нам взять Collection, и использовать stream() метод на нем, чтобы получить поток всех его элементов. В нем есть ряд полезных методов, таких как filter(),map() и forEach(), которые позволяют нам использовать лямбда-операции на содержание.

у меня есть код, который выглядит примерно так (упрощенно):

set.stream().filter(item -> item.qualify())
    .map(item -> (Qualifier)item).forEach(item -> item.operate());
set.removeIf(item -> item.qualify());

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

если он находится в Документации, я может быть с видом на него.

кто - нибудь более знаком с API видит что-то подобное?

8 53

8 ответов:

вы можете сделать это так:

set.removeIf(item -> {
    if (!item.qualify())
        return false;
    item.operate();
    return true;
});

если item.operate() всегда возвращает true вы можете сделать это очень емко.

set.removeIf(item -> item.qualify() && item.operate());

однако мне не нравятся эти подходы, так как не сразу понятно, что происходит. Лично я бы продолжал использовать for петли и Iterator для этого.

for (Iterator<Item> i = set.iterator(); i.hasNext();) {
    Item item = i.next();
    if (item.qualify()) {
        item.operate();
        i.remove();
    }
}

в одной строке нет, но, возможно, вы могли бы использовать partitioningBy взыскатель:

Map<Boolean, Set<Item>> map = 
    set.stream()
       .collect(partitioningBy(Item::qualify, toSet()));

map.get(true).forEach(i -> ((Qualifier)i).operate());
set = map.get(false);

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

в противном случае я думаю, что ваш подход относительно хорош.

то, что вы действительно хотите сделать, это разделить свой набор. К сожалению, в Java 8 разделение возможно только с помощью терминального метода "collect". Вы в конечном итоге с чем-то вроде этого:

// test data set
Set<Integer> set = ImmutableSet.of(1, 2, 3, 4, 5);
// predicate separating even and odd numbers
Predicate<Integer> evenNumber = n -> n % 2 == 0;

// initial set partitioned by the predicate
Map<Boolean, List<Integer>> partitioned = set.stream().collect(Collectors.partitioningBy(evenNumber));

// print even numbers
partitioned.get(true).forEach(System.out::println);
// do something else with the rest of the set (odd numbers)
doSomethingElse(partitioned.get(false))

обновление:

Scala версия кода выше

val set = Set(1, 2, 3, 4, 5)
val partitioned = set.partition(_ % 2 == 0)
partitioned._1.foreach(println)
doSomethingElse(partitioned._2)`

существует множество подходов. Если использовать myList.удалить (элемент) должен переопределить equals (). Второе, что я предпочитаю:

allList.removeIf(item -> item.getId().equals(elementToDelete.getId()));

удачи и счастливого кодинга :)

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

если я правильно понял ваш вопрос:

set = set.stream().filter(item -> {
    if (item.qualify()) {
        ((Qualifier) item).operate();
        return false;
    }
    return true;
}).collect(Collectors.toSet());

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

вы не можете удалить элементы из исходного потока с потоком. Из документация:

большинство операций потока принимают параметры, описывающие поведение, заданное пользователем..... Чтобы сохранить правильное поведение, эти поведенческие параметры:

  • должны быть без помех (они не изменяют источник потока); и
  • в большинстве случаев должны быть без гражданства (их результат не должен зависеть от любого государства, которое может измениться во время выполнения потока).

Я вижу озабоченность ясности Павла при использовании потоков, заявленных в верхнем ответе. Возможно, добавление объясняющей переменной немного проясняет намерения.

set.removeIf(item -> {
  boolean removeItem=item.qualify();
  if (removeItem){
    item.operate();
  }
  return removeItem;
});