Как создать коллекцию из Iterable в Java? - PullRequest
6 голосов
/ 21 октября 2011

Например, у меня есть набор геометрических фигур:

Set<Figure> figures;

Существует два типа рисунков: квадрат и круг.

Я хочу получить набор квадратов, используя коллекции Google:

Iterables.filter(figures,squarePredicate);

Но метод фильтра возвращает Iterable ... Как я могу создать Set из Iterable?(без использования цикла в Iterable)

Ответы [ 6 ]

15 голосов
/ 21 октября 2011

Я думаю, вам нужно переосмыслить свои требования. Вам нужен набор квадратов. Почему?

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

Есть только две возможные причины: либо вы работаете с API, которому нужен параметр Set (или Collection), либо вам нужно каким-то образом отобразить размер набора.

В этих случаях используйте Sets.newHashSet(iterable) для создания набора (с одной стороны, разумеется, что требуется полная итерация, с другой стороны: вам все равно понадобится полная итерация в одной точке, когда вы перебираем значения, так почему бы не сделать это сейчас?). В противном случае просто используйте Iterable и забудьте о множестве.

7 голосов
/ 21 октября 2011

Если у вас есть Set, вы можете использовать Sets.filter вместо Iterables.filter и получить Set результат.Это Set является живым представлением, как результат Iterables.filter, но у него есть Set свойства, такие как быстрый contains метод.

Для создания копии , содержащей* только элементы, которые соответствуют предикату, вы можете использовать ImmutableSet.copyOf или Sets.newHashSet, как предлагали другие.

5 голосов
/ 21 октября 2011

Guava's Iterables.filter() преднамеренно возвращает итерируемое "представление". У этого подхода есть два преимущества:

  • вы перебираете элементы только тогда, когда вам это действительно нужно (например, вы можете связывать вызовы Iterables.filter () и Iterables.transform () и повторять только один раз в конце).
  • вы можете создать соответствующую коллекцию из вида, используя что-то вроде ImmutableSet.copyOf(Iterables.filter(..., ...)), Sets.newHashSet(Iterables.filter(..., ...)) или Lists.newArrayList(Iterables.filter(..., ...)). Iterables.filter () позволяет вам выбрать точную коллекцию, а не возвращать произвольную.

Я также заметил, что вы, похоже, используете Iterables.filter (Итерируемый нефильтрованный предикат Predicate) с предикатом для фильтрации экземпляров определенного типа. Вас также может заинтересовать перегрузка Iterables.filter (Iterable без фильтра, тип класса) , которая фильтрует все экземпляры данного типа и возвращает Iterable с более конкретным универсальным типом. Это позволяет вам избегать неловких приведений.

1 голос
/ 11 октября 2014

Вы можете отфильтровать набор и собрать в другой набор с потоками Java 8:

Set<Number> integers = numbers.stream()
    .filter(x -> x instanceof Integer)
    .collect(Collectors.toSet());

Возвращенный набор является копией, а не просмотром в реальном времени.

Обратите внимание, что в отличие, например, от FluentIterable.filter в Guava, результирующий набор равен Set<Integer>, поскольку Java не знает, что вы отфильтровали все нецелые числа. Если вам нужны Set<Integer>, , вы должны сопоставить после фильтрации .

Set<Integer> integers = numbers.stream()
    .filter(x -> x instanceof Integer)
    .map(x -> (Integer)x)
    .collect(Collectors.toSet());

(Вы можете объединить фильтр и карту в flatMap, но это введет временный объект потока для целого числа и не будет более кратким.)

1 голос
/ 21 октября 2011

Может, попробовать вместо этого CollectionUtils.filter() из утилит apache collection? Вы можете использовать его для Set или использовать результирующую коллекцию в конструкторе Set.

1 голос
/ 21 октября 2011

Используйте что-то вроде Sets.newHashSet(Iterable) (или как вам нужно).

...