Возврат потока из уровня обслуживания [дубликат]

Возможно, этот способ?

  private void Pager & lt; t & gt; (IEnumerable & lt; t & tnnum)  
128
задан Stuart Marks 27 July 2014 в 04:48
поделиться

8 ответов

Ответ, как всегда, «это зависит». Это зависит от того, насколько большой будет возвращенная коллекция. Это зависит от того, изменяется ли результат со временем и насколько важна согласованность возвращаемого результата. И это сильно зависит от того, как пользователь может использовать ответ.

Во-первых, обратите внимание, что вы всегда можете получить сборку из потока, и наоборот:

// If API returns Collection, convert with stream()
getFoo().stream()...

// If API returns Stream, use collect()
Collection<T> c = getFooStream().collect(toList());

Итак, вопрос в том, что более полезно для ваших абонентов.

Если ваш результат может быть бесконечным, есть только один выбор: Stream.

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

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

Даже если вы знаете, что пользователь будет повторять его несколько раз или иным образом держать его вокруг, вы все равно можете захотеть вернуть Stream вместо этого, поскольку тот факт, что какая-либо коллекция вы выбрали для ее размещения (например, , ArrayList) может быть не той формой, которую они хотят, а затем вызывающий должен ее скопировать. если вы вернете поток, они могут сделать collect(toCollection(factory)) и получить его в той форме, в которой они хотят.

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

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

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

181
ответ дан Brian Goetz 15 August 2018 в 21:40
поделиться
  • 1
    Как я уже сказал, есть несколько случаев, когда он не будет летать, например, когда вы хотите вернуть моментальный снимок во время движущейся цели, особенно когда у вас есть сильные требования к согласованности. Но большую часть времени Stream кажется более общим выбором, если вы не знаете что-то конкретное о том, как он будет использоваться. – Brian Goetz 10 July 2014 в 17:24
  • 2
    @Marko Даже если вы ограничите свой вопрос так узко, я до сих пор не согласен с вашим заключением. Возможно, вы предполагаете, что создание Stream как-то намного дороже, чем упаковка коллекции с неизменяемой оболочкой? (И даже если вы этого не сделаете, представление потока, которое вы получаете на обертке, хуже, чем то, что вы получаете от оригинала, потому что UnmodifiableList не отменяет spliterator (), вы фактически потеряете весь параллелизм.) Нижняя строка: будьте осторожны смещения знакомства; вы знаете коллекцию годами, и это может заставить вас не доверять новичкам. – Brian Goetz 10 July 2014 в 18:54
  • 3
    @MarkoTopolnik Конечно. Моя цель состояла в том, чтобы решить общий вопрос API-дизайна, который становится часто задаваемым вопросом. Что касается стоимости, обратите внимание, что если вы еще не имеете материализованную коллекцию, которую вы можете вернуть или обернуть (OP делает, но часто ее нет), материализация коллекции в методе getter не является дешевле, чем возвращать поток и позволять абоненту материализовать один (и, конечно, ранняя материализация может быть намного дороже, если вызывающий не нуждается в ней или если вы возвращаете ArrayList, но вызывающий хочет TreeSet.) Но Stream является новым, и люди часто предположим, что он больше $$$, чем он есть. – Brian Goetz 10 July 2014 в 19:14
  • 4
    @MarkoTopolnik В то время как встроенная память является очень важным прецедентом, есть также некоторые другие случаи, которые имеют хорошую поддержку параллелизации, например, не упорядоченные генерируемые потоки (например, Stream.generate). Однако, когда потоки плохо подходят, это реактивный вариант использования, когда данные поступают со случайной задержкой. Для этого я бы предложил RxJava. – Brian Goetz 10 July 2014 в 19:38
  • 5
    @MarkoTopolnik Я не думаю, что мы не согласны, разве что, возможно, вам понравилось, чтобы мы сосредоточили свои усилия немного по-другому. (Мы привыкли к этому, не можем сделать всех людей счастливыми.) Центр дизайна для Streams сосредоточен на структурах данных в памяти; дизайн-центр RxJava фокусируется на событиях, генерируемых извне. Оба являются хорошими библиотеками; также оба не очень хорошо себя чувствуют, когда вы пытаетесь применить их к случаям из своего центра дизайна. Но только потому, что молот - ужасный инструмент для иглоукалывания, это не означает, что с молотом что-то не так. – Brian Goetz 10 July 2014 в 21:07

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

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

1
ответ дан designbygravity 15 August 2018 в 21:40
поделиться

У меня, вероятно, было бы 2 метода, один для возврата Collection и один, чтобы вернуть коллекцию как Stream.

class Team
{
    private List<Player> players = new ArrayList<>();

// ...

    public List<Player> getPlayers()
    {
        return Collections.unmodifiableList(players);
    }

    public Stream<Player> getPlayerStream()
    {
        return players.stream();
    }

}

Это лучшее из обоих миров. Клиент может выбрать, хотят ли они List или Stream, и им не нужно делать дополнительное создание объекта для создания неизменяемой копии списка, чтобы получить Stream.

Это также добавляет только 1 больше метода для вашего API, поэтому у вас не слишком много методов

-4
ответ дан dkatzel 15 August 2018 в 21:40
поделиться
  • 1
    Потому что он хотел выбирать между этими двумя вариантами и задавал все плюсы и минусы каждого из них. Кроме того, он предоставляет каждому лучшее понимание этих концепций. – Libert Piou Piou 10 July 2014 в 21:20
  • 2
    Пожалуйста, не делай этого. Представьте себе API! – François Gautier 8 November 2016 в 16:50

Я думаю, это зависит от вашего сценария. Может быть, если вы сделаете свой Team вариант Iterable<Player>, этого достаточно.

for (Player player : team) {
    System.out.println(player);
}

или в функциональном стиле:

team.forEach(System.out::println);

Но если вы хотите более полный и свободный api, поток может быть хорошим решением.

0
ответ дан gontard 15 August 2018 в 21:40
поделиться
  • 1
    Обратите внимание, что в коде, опубликованном OP, количество игроков почти бесполезно, кроме как оценка («1034 игрока играют сейчас, нажмите здесь, чтобы начать!») Это связано с тем, что вы возвращаете неизменный вид изменчивой коллекции , поэтому счет, который вы получаете, теперь может не равняться счету три микросекунды с этого момента. Поэтому, возвращая коллекцию, вы получаете «простой». способ получить к счету (и действительно, stream.count() тоже довольно легко), это число не очень важно для чего-либо другого, кроме отладки или оценки. – Brian Goetz 10 July 2014 в 16:15
  • 2
    ok, я обнаружил метод count, обновленный. – gontard 10 July 2014 в 17:59

Были ли потоки, которые всегда были «завершены» внутри того же выражения, в котором они были созданы?

Вот как они используются в большинстве примеров.

Примечание: возврат потока не отличается от возвращаемого Итератора (допускается с гораздо большей выразительностью)

IMHO лучшим решением является инкапсуляция, почему вы это делаете, а не возвратите коллекцию.

, например

public int playerCount();
public Player player(int n);

, или если вы намерены считать их

public int countPlayersWho(Predicate<? super Player> test);
1
ответ дан Peter Lawrey 15 August 2018 в 21:40
поделиться
  • 1
    Проблема с этим ответом заключается в том, что автор должен предугадать каждое действие, которое клиент хочет сделать, это значительно увеличит количество методов в классе. – dkatzel 10 July 2014 в 15:37
  • 2
    @dkatzel Это зависит от того, являются ли конечные пользователи автором или кем-то, с кем они работают. Если конечные пользователи непознаваемы, вам нужно более общее решение. Вы все равно можете ограничить доступ к основной коллекции. – Peter Lawrey 10 July 2014 в 19:41

У меня есть несколько моментов, чтобы добавить к превосходный ответ Брайана Гетца .

. Очень часто возвращать Stream из вызова метода стиля «getter». См. Страницу использования потока использования в Java 8 javadoc и найдите «методы ..., которые возвращают поток» для пакетов, отличных от java.util.Stream. Эти методы обычно относятся к классам, которые представляют или могут содержать несколько значений или агрегатов чего-либо. В таких случаях API обычно возвращают коллекции или массивы из них. По всем причинам, которые Брайан отметил в своем ответе, очень гибко добавлять сюда методы Stream-return. У многих из этих классов уже есть методы, основанные на коллекции или массиве, поскольку классы предшествуют API Streams. Если вы разрабатываете новый API, и имеет смысл предоставлять методы Stream-return, возможно, нет необходимости добавлять методы возврата коллекции.

Брайан упомянул стоимость «материализации» значения в коллекцию. Чтобы усилить этот момент, на самом деле существуют две затраты: стоимость хранения значений в коллекции (выделение и копирование памяти), а также стоимость создания значений в первую очередь. Последнюю стоимость часто можно уменьшить или избежать, воспользовавшись стремлением Stream к лени. Хорошим примером этого являются API-интерфейсы в java.nio.file.Files:

static Stream<String>  lines(path)
static List<String>    readAllLines(path)

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

try (Stream<String> lines = Files.lines(path)) {
    List<String> firstTen = lines.limit(10).collect(toList());
}

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

. Идиома, которая, кажется, появляется, - это имя методов возврата потока после множественного числа имени того, что оно представляет или содержит, без префикса get. Кроме того, в то время как stream() является разумным именем для метода возврата потока, когда есть только один возможный набор значений, возвращаемых, иногда есть классы, которые имеют агрегаты нескольких типов значений. Например, предположим, что у вас есть объект, содержащий как атрибуты, так и элементы. Вы можете предоставить два API-интерфейса, возвращающих поток:

Stream<Attribute>  attributes();
Stream<Element>    elements();
61
ответ дан Stuart Marks 15 August 2018 в 21:40
поделиться
  • 1
    Отличные очки. Можете ли вы сказать больше о том, где вы видите, что именования именования возникают, и сколько тяги (пара?) Оно поднимает? Мне нравится идея соглашения об именовании, в котором очевидно, что вы получаете поток против коллекции, хотя я также часто ожидаю завершения IDE в «get & quot; чтобы рассказать мне, что я могу получить. – Joshua Goldberg 1 February 2016 в 16:59
  • 2
    Я также очень заинтересован в том, что именование именования – elect 13 July 2016 в 16:47
  • 3
    @JoshuaGoldberg JDK, похоже, принял эту идиоматическую идиому, хотя и не исключительно. Рассмотрим: CharSequence.chars () и .codePoints (), BufferedReader.lines () и Files.lines () существуют в Java 8. В Java 9 были добавлены следующие: Process.children (), NetworkInterface.addresses ( ), Scanner.tokens (), Matcher.results (), java.xml.catalog.Catalog.catalogs (). Добавлены другие методы возврата потока, которые не используют эту идиому - Scanner.findAll () приходит на ум, - но идиома множественного числа существительных, по-видимому, получила справедливое использование в JDK. – Stuart Marks 14 July 2016 в 01:09
  • 4
    @elect См. выше. – Stuart Marks 14 July 2016 в 01:09

В контексте команды / предприятия / многоразовых классов:

Обычно разрабатывают классы без учета того, что клиенты могут принять в отношении класса. Если вы вернете поток, вы вернете потенциально бесконечный список, и клиенты должны рассматривать это как потенциально бесконечный список. Это нарушение безопасности кода и хороший вкус для любого другого класса или другого метода для запуска stream.collect(toList()) в потоке, который он не создал сам, слепо предположив, что он будет конечным. Если это был желательный шаблон кода, потоки должны иметь некоторый метод, например isFinite(), чтобы защищать от ошибочных предположений.

Хуже того, возвращение потока во избежание материализации одинаково плохо, потому что кажется слишком легким материализует поток с .collect(), то есть пока класс обслуживания избегает материализации, у класса клиента может возникнуть соблазн сделать это с высокой стоимостью, в частности, если вы постоянно возвращаете потоки в качестве стиля кода и больше не можете сказать, какой поток возвращается должны быть материализованы, а какие - нет. Классические интерфейсы, которые указывают на плохую материализацию, - это java.util.Iterator (хотя commons-lang определяет IteratorUtils.toList ()).

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

То, что потоки более удобны для функционального программирования, не означает, что они должны быть предпочтительными, это означает, что JDK должен расширить его API-интерфейс коллекции, позволяющий выполнять те же операции, чтобы клиентам не приходилось называть collection.stream().....

Для домашних заданий, строго личных проектов или другого неиспользованного кода

From простая алгоритмическая перспектива, игнорируя стиль кода, читабельность или проблемы OO-Design, предпочитая Streams, как в принятом ответе, в порядке. Но я не вижу, как это можно сделать общей рекомендацией по StackOverflow

2
ответ дан tkruse 15 August 2018 в 21:40
поделиться

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

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

0
ответ дан Vazgen Torosyan 15 August 2018 в 21:40
поделиться
  • 1
    есть ли причина, которая полностью цитируется? есть ли источник? – Xerus 7 June 2017 в 20:40
Другие вопросы по тегам:

Похожие вопросы: