3

Sometimes, when source code gets complicated, I find it confusing to read statements like these:

Set<Integer> odds = new HashSet<>(ints); // not yet true
odds.removeAll(evens); // now it's true

I was wondering if there's a clever way to avoid a line where odds contains also even values. Something similar to this:

(Set<Integer> odds = new HashSet<>(ints)).removeAll(evens); // doesn't compile

I could use double brace initialization,

Set<Integer> odds = new HashSet<Integer>(ints) {{ removeAll(evens); }};

but that's obviously bad for multiple reasons.

Here's another example hack that compiles, but it looks more like a joke:

Set<Integer> odds = (odds = new HashSet<>(ints)).retainAll(evens) ? odds : odds

The last one that came to my mind (while writing this) seems to be ok, although it uses two lines:

Set<Integer> odds;
(odds = new HashSet<Integer>(ints)).removeAll(evens);

Any other ideas?

Community
  • 1
  • 1
steffen
  • 16,138
  • 4
  • 42
  • 81

2 Answers2

3

Use a stream:

Set<Integer> odds =
    ints.stream().filter(x -> x % 2 == 1).collect(Collectors.toSet());

If you want to partition the set into odds and evens at the same time:

Map<Boolean, Set<Integer>> oddsAndEvens =
    ints.stream().collect(
        Collectors.partitioningBy(x -> x % 2 == 0, Collectors.toSet()));
Set<Integer> evens = oddsAndEvens.get(true);
Set<Integer> odds = oddsAndEvens.get(false);
Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • At least for a `retainAll()` operation I think this is a good approach: `ints.stream().filter(evens::contains).collect(Collectors.toSet())`. – steffen May 03 '17 at 15:22
  • @steffen that assumes that you're able to build `evens`; and then surely you've got the same problem in constructing that. – Andy Turner May 03 '17 at 15:32
  • @AndyTurner In the examples we also do have `evens` already, so that makes no difference. Actually I find your answer helpful, I just wanted to add that a method reference could be a bit less unclear as mud as @Michael put it. For `removeAll()`, we'd need [to negate the predicate](http://stackoverflow.com/questions/21488056/how-to-negate-a-method-reference-predicate). – steffen May 04 '17 at 09:57
  • While this addresses the odd/even case, what about the *generic case* where you want to create a new set from the substraction of one set from another set (all in a single line)? – Michael May 04 '17 at 10:16
  • @Michael I find this approach more generic since you can define the result yourself, be it by filtering with predicates like contains, or by a limitation (first random 5) or by element transformation. – steffen May 04 '17 at 12:45
  • @Michael `ints.stream().filter(x -> !evens.contains(x)).collect(Collectors.toSet())`. – Andy Turner May 04 '17 at 12:55
-2

I'm not sure why do you are trying to over complicate the matter. Provided you can get hold of the collection of evens, you can surely do the same for odds.

Then you can simply do:

Set<Integer> oddsSet = new HashSet<>(odds);

Alternatively, you could squeeze your last example into a single line:

Set<Integer> odds; (odds = new HashSet<Integer>(ints)).removeAll(evens);

I do apologise for my ignorance but I think this problem is of an academic nature and the other suggested answers might not necessarily compensate for not having an extra line at all cost, such as the use of streams will almost certainly underperform against a genuine collection.

RZet
  • 914
  • 9
  • 11
  • If you disregard the odds and evens, I think it's perfectly reasonable to want to be able to create a `SetC = SetA - SetB` in an idiomatic way – Michael May 04 '17 at 13:07
  • @Michael I'm not daft and did get the point of the question but thought people would find my answer rather amusing lol; I don't think downvoting was fair. – RZet May 04 '17 at 13:13
  • Jokey answers should be left in the comments on the question (if at all!). I'm trying to find a source on that, but without much luck. Nothing personal! – Michael May 04 '17 at 13:22