Можно ли создать потоковую реализацию, которая подсчитывает их элементы за одну операцию? - PullRequest
6 голосов
/ 11 апреля 2019

В: Можно ли создать реализацию Stream, которая подсчитывает их элементы в одной операции, а не подсчитывает каждый элемент в потоке?

Я пришел к этому, хотя, когда я попытался сравнить два метода в списке:

  • size()

  • count()

Stream::count операция терминала подсчитывает количество элементов в потоке. Сложность операции часто составляет O (N) , что означает, что число подопераций пропорционально количеству элементов в Stream .

List::size метод имеет сложность O (1) , что означает, что независимо от количества элементов в списке, метод size() будет возвращаться в постоянное время.

   List<Integer> list = IntStream.range(0, 100).boxed().collect(toList());
    System.out.println(list.size());
    System.out.println(list.stream().count());

size() заняло относительно меньше времени, чем count(), поэтому есть ли какой-нибудь возможный способ создать реализацию Stream, которая подсчитывает их элементы за одну операцию и имеет сложность O (1) ?


Редактировать Статья для ответа Да :

Можно создать Stream реализацию, которая считает их элементы в одной операции O (1) вместо подсчета каждого и каждый элемент в потоке. Это может улучшить производительность значительно, особенно для потоков со многими элементами.

1 Ответ

8 голосов
/ 11 апреля 2019

Это уже происходит в Java 9 и новее (учитывая реализацию OpenJDK, которая также является основой для Oracle JDK).

Если вы хотите подобную операцию, вы можете использовать, например,

public static long count(BaseStream<?,?> s) {
    Spliterator<?> sp = s.spliterator();
    long c = sp.getExactSizeIfKnown();
    if(c >= 0) return c;
    final class Counter implements Consumer<Object>,
        IntConsumer, LongConsumer, DoubleConsumer { // avoid boxing where possible
        long count;
        public void accept(Object t) { count++; }
        public void accept(int value) { count++; }
        public void accept(long value) { count++; }
        public void accept(double value) { count++; }
    }
    Counter c = new Counter();
    sp.forEachRemaining(c);
    return c.count;
}

Вы можете проверить, что он не будет обрабатывать все элементы с помощью

System.out.println(count(IntStream.range(0, 100).peek(System.out::println)));
System.out.println(count(Stream.of("a", "b", "c").peek(System.out::println)));

при вставке операции filter, например

System.out.println(count(Stream.of("a", "b", "c")
    .peek(System.out::println).filter(x -> true)));

сделает подсчет непредсказуемым и потребует обхода.

Как сказано выше, в JDK 9 или новее вы можете просто использовать

System.out.println(Stream.of("a", "b", "c").peek(System.out::println).count());

и

System.out.println(Stream.of("a", "b", "c")
    .peek(System.out::println).filter(x -> true).count());

чтобы увидеть, что обход не происходит, когда счет предсказуем.

...