Возвращает список одного элемента, если условие фильтра потока не возвращает никаких результатов. - PullRequest
0 голосов
/ 13 мая 2018

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

В настоящее время я выдумываю это, собирая результат фильтра вlist, если список пуст, создайте мой новый список по умолчанию и верните его в виде потока.Если список не пустой, преобразуйте результат обратно в поток, чтобы передать его обратно.

Что является более потоковым способом для достижения этой цели без необходимости переходить в списки и обратно в потоки?

Ответы [ 2 ]

0 голосов
/ 13 мая 2018

Лучшее решение, позволяющее избежать необходимости собирать весь поток, избегать потери исходных характеристик Stream и сохранять (большую часть) его оптимизации, состоит в реализации пользовательского Spliterator, который обрабатывает значение по умолчанию в случае, если исходный поток Spliterator пусто:

public static <E> Stream<E> defaultIfEmpty(Stream<E> source, Supplier<? extends E> other) {
    final boolean parallel = source.isParallel();
    final Spliterator<E> originalSpliterator = source.spliterator();

    // little optimization for streams of known size
    final long size = originalSpliterator.getExactSizeIfKnown();
    if (size == 0) {
        // source already reports that it is empty
        final Stream<E> defaultStream = Stream.of(other.get());
        if (parallel) {
            return defaultStream.parallel();
        } else {
            return defaultStream;
        }
    }

    final Spliterator<E> spliterator;
    if (size > 0) {
        // source already reports that it is non-empty
        spliterator = originalSpliterator;
    } else {
        // negative means unknown, so wrap the source
        spliterator = wrap(originalSpliterator, other);
    }
    return StreamSupport.stream(spliterator, parallel);
}

private static <E> Spliterator<E> wrap(final Spliterator<E> spliterator, final Supplier<? extends E> other) {
    return new Spliterator<E>() {
        boolean useOther = true;
        @Override
        public boolean tryAdvance(final Consumer<? super E> action) {
            boolean couldAdvance = spliterator.tryAdvance(action);
            if (!couldAdvance && useOther) {
                useOther = false;
                action.accept(other.get());
                return true;
            }
            useOther = false;
            return couldAdvance;
        }

        @Override
        public Spliterator<E> trySplit() {
            if (!useOther) {
                // we know the original spliterator was not empty, we will thus never need the default
                return spliterator.trySplit();
            }
            Stream.Builder<E> builder = Stream.builder();
            if (spliterator.tryAdvance(builder)) {
                useOther = false;
                return builder.build().spliterator();
            } else {
                // spliterator is empty, but we will handle it in tryAdvance
                return null;
            }
        }

        @Override
        public long estimateSize() {
            long estimate = spliterator.estimateSize();
            if (estimate == 0 && useOther) {
                estimate = 1;
            }
            return estimate;
        }

        @Override
        public int characteristics() {
            // we don't actually change any characteristic of the original spliterator
            return spliterator.characteristics();
        }
    };
}

Пример использования:

System.out.println(defaultIfEmpty(Stream.empty(), () -> 42).collect(toList()));
System.out.println(defaultIfEmpty(Stream.of(1, 2, 3), () -> 42).collect(toList()));
System.out.println(defaultIfEmpty(Stream.iterate(1, i -> i+1).parallel().filter(i -> i%3 == 0).limit(10), () -> 42).collect(toList()));
System.out.println(defaultIfEmpty(Stream.iterate(1, i -> i+1).parallel().limit(3).filter(i -> i%4 == 0), () -> 42).collect(toList()));

Выход:

[42]
[1, 2, 3]
[3, 6, 9, 12, 15, 18, 21, 24, 27, 30]
[42]
0 голосов
/ 13 мая 2018

Насколько я понимаю, вам нужен метод, аналогичный DefaultIfEmpty в C #.К сожалению, в Stream API такого метода нет, но, к счастью, кто-то уже реализовал что-то подобное.

Используя метод defaultIfEmpty из @ Stuart Marks answer , пример использования довольно прост.

static <T> Stream<T> defaultIfEmpty(Stream<T> stream, Supplier<T> supplier) {
    Iterator<T> iterator = stream.iterator();
    if (iterator.hasNext()) {
        return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false);
    } else {
        return Stream.of(supplier.get());
    }
}

Например, для примера, предположим, что у вас есть списокцелые числа:

List<Integer> integerList = new ArrayList<>(Arrays.asList(1,3,5,7,9)); 

и вам нужен поток с одним значением, если в вышеупомянутом списке нет четных чисел.

Вариант использования:

Stream<Integer> result = defaultIfEmpty(integerList.stream()
                                 .filter(e -> e %2 == 0), () -> 99); 

Это даст Stream<Integer> с одним элементом 99, так как операция filter вернула пустой поток.затем вы можете связать дальнейшие операции в потоке, возвращенном с defaultIfEmpty, то есть

defaultIfEmpty(integerList.stream()
                       .filter(e -> e %2 == 0), () -> 99)
.map(x -> x*2)
...
...

, или применить последующие defaultIfEmpty методы к:

 Stream<Integer> result = defaultIfEmpty(
                   defaultIfEmpty(integerList.stream()
              .filter(e -> e %2 == 0), 
      () -> 99).map(x -> x* 2)..., ()-> -1);

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...