6

For example, I have set of geometrical figures:

Set<Figure> figures;

There are two types of Figure: Square and Circle.

I want to get set of squares using google collections:

Iterables.filter(figures,squarePredicate);

But filter method return Iterable... How can I create Set from Iterable? (without using loop on Iterable)

6 Answers 6

15

I think you need to rethink your requirements. You need a set of squares. Why?

A set gives you uniqueness and iteration, nothing more. You have uniqueness in your Iterable, because the source is a set, and you can iterate over the items in an Iterable. So why would you need the set?

There are only two possible reasons: either you are working with an API that needs a Set (or Collection) parameter, or you need to somehow display the Set's size.

In these cases, use Sets.newHashSet(iterable) to create a Set (on one hand of course that requires a full iteration, on the other hand: you will need a full iteration at one point anyway when you are iterating over the values, so why not do it now?). Otherwise, just use the Iterable and forget about a Set.

Sign up to request clarification or add additional context in comments.

2 Comments

This code is for example. I have set of objects of two kinds. I want filter one kind of objects with google collections, and then get second kind of objects with sets substraction.
Small typo: Sets.newHashSet(iterable)
7

If you have a Set, you can use Sets.filter rather than Iterables.filter and get a Set result. That Set is a live view, like the result of Iterables.filter, but it has Set properties such as a fast contains method.

To create a copy containing only the elements that match the predicate, you can use ImmutableSet.copyOf or Sets.newHashSet as others have suggested.

Comments

5

Guava's Iterables.filter() deliberately returns an Iterable "view". There are two benefits to this approach:

  • you only iterate over the elements when you really need to (e.g. you can chain Iterables.filter() and Iterables.transform() calls, and iterate only once at the end).
  • you can create the appropriate collection from the view, using something like ImmutableSet.copyOf(Iterables.filter(..., ...)), Sets.newHashSet(Iterables.filter(..., ...)), or Lists.newArrayList(Iterables.filter(..., ...)). Iterables.filter() lets you choose the precise collection needed, instead of returning an arbitrary one.

I also noticed that you seem to use Iterables.filter(Iterable unfiltered, Predicate predicate) with a predicate to filter instances of a specific type. You might also be interested in the Iterables.filter(Iterable unfiltered, Class type) overload, that filters all instances of the given type, and returns an Iterable with the more specific generic type. This lets you avoid awkward casts.

Comments

1

Use something like Sets.newHashSet(Iterable) (or whatever you need it to be).

Comments

1

Maybe try CollectionUtils.filter() from apache collection utils instead? You can use it on a Set, or use the resulting collection in the Set constructor.

4 Comments

OP did kind of state "using Google Collections".
The question is about Guava, not Commons / Collections. And the problem is the same: you need a full iteration to apply the filter.
I believe he was describing the problem he is encountering with google collection rather than saying it is mandatory to use it. I am just suggesting going another direction in case it is not mandatory. Just a suggestion.
What do you mean by "you need a full iteration"? The iteration is made by the filter method itself, you don't need to write an iteration. And whatever tool you use, an iteration will always be made one way or another to filter the collection.
1

You can filter a set and collect into another set with Java 8 streams:

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

The returned set is a copy, not a live view.

Note that unlike, e.g., Guava's FluentIterable.filter, the resulting set is a Set<Integer> because Java doesn't know you've filtered out all the non-integers. If you need a Set<Integer>, you have to map after filtering.

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

(You could combine the filter and map into a flatMap, but that would introduce a temporary stream object per integer, and isn't any more concise.)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.