Removing a single element from an ImmutableSet

4,592 views
Skip to first unread message

Patrick Arnesen

unread,
Jun 23, 2010, 6:20:04 PM6/23/10
to guava-discuss
Recently I needed to produce an immutable set one element smaller than
a given ImmutableSet, and found that the code needed could be shorter:

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;

public class RemoveOne {
public static void main(String[] args) {
ImmutableSet<String> set = ImmutableSet.of("a", "b", "c");
set = ImmutableSet.copyOf(Sets.difference(set,
ImmutableSet.of("c")));
System.out.println(set);
}
}

this produces [a, b]

As you can see, I had to create a set with one element in it, then do
a set difference, then wrap the result in an immutable set. Is there a
more direct way to produce the same result?

Perhaps something similar to the builder concept:

set = set.modifer().remove("a").build();

Craig Berry

unread,
Jun 23, 2010, 6:31:59 PM6/23/10
to guava-...@googlegroups.com
On Wed, Jun 23, 2010 at 3:20 PM, Patrick Arnesen
<patrick...@gmail.com> wrote:
> Recently I needed to produce an immutable set one element smaller than
> a given ImmutableSet, and found that the code needed could be shorter:

How about

set = Sets.difference(set, ImmutableSet.of("c")).immutableCopy();

It's almost the same thing, of course, but a bit terser and clearer.

--
Craig Berry
Software Engineer
Google (Santa Monica CA)

Kevin Bourrillion

unread,
Jun 23, 2010, 6:32:46 PM6/23/10
to guava-...@googlegroups.com
On Wed, Jun 23, 2010 at 3:20 PM, Patrick Arnesen <patrick...@gmail.com> wrote:

set = ImmutableSet.copyOf(Sets.difference(set, ImmutableSet.of("c")));

Slightly nicer is:

set = Sets.difference(set, ImmutableSet.of("c")).immutableCopy();

 
As you can see, I had to create a set with one element in it, then do

a set difference, then wrap the result in an immutable set. Is there a
more direct way to produce the same result?

Perhaps something similar to the builder concept:

set = set.modifer().remove("a").build();

I think there's at least a feature request to support removes in ImmutableSet.Builder.  I have mixed feelings about it.  I'm a little concerned that this will lead to more people passing these builders around as if they're collections, when in those cases they really should just be using a plain old HashSet and then ImmutableSet.copyOf'ing that later.  But, if that feature would make your life better, please check to see if it's filed and file it if it's not.


--
Kevin Bourrillion @ Google
http://guava-libraries.googlecode.com

Tim Harsch

unread,
Jun 24, 2010, 12:33:42 PM6/24/10
to guava-...@googlegroups.com
I'm curious what is the need for the immutableCopy?  Sets.difference javadoc says "Returns an unmodifiable view of the difference of two sets."  and both sets passed to 'difference' are Immutable.  If you "Refer to Objects by their Interface" (EJ Item 52) then I would think making an ImmutableCopy is redundant. (But more likely I'm just missing something :-)




--
guava-...@googlegroups.com.
http://groups.google.com/group/guava-discuss?hl=en
unsubscribe: guava-discus...@googlegroups.com
 
This list is for discussion; for help, post to Stack Overflow instead:
http://stackoverflow.com/questions/ask
Use the tag "guava".

Kevin Bourrillion

unread,
Jun 24, 2010, 12:40:45 PM6/24/10
to guava-...@googlegroups.com
It's true that there is no functional benefit, but there might be a performance benefit depending on how and how often the set is queried.

tangent:

Also, when EJ says "by interface", it really means, "by the most general type that still conveys the important behavioral semantics you care about."  In this case, that's often ImmutableSet -- the immutability guarantee is a crucial part of its contract, not just an implementation detail.

In fact, ImmutableSet would be an interface, if not for the fact that this would allow anyone's mutable implementation to implement it and thus wreak havoc.

Tim Harsch

unread,
Jun 24, 2010, 12:48:32 PM6/24/10
to guava-...@googlegroups.com
Are you saying there may be a performance benefit thinking in the general case where the underlying sets might be large and/or high load?  Because in this particular case both the underlying sets are quite small... though I don't know if that would be the only consideration because I'm not real familiar with the implementation of Sets.Setview.

Kevin Bourrillion

unread,
Jun 24, 2010, 12:52:19 PM6/24/10
to guava-...@googlegroups.com
I certainly agree that for small sets, the total impact is likely to be negligible unless you have a server that does nothing but sit around querying small sets all day long. :-)   The .immutableCopy() method is just there to use when and if you want it.  If your set is exposed publicly in any way, it can be good to expose it as an ImmutableSet so its guarantee of immutability is communicated.

Craig Berry

unread,
Jun 24, 2010, 12:56:32 PM6/24/10
to guava-...@googlegroups.com
If I could go back in time and advise the Java designers, one of my
first requests would be that they make mutable classes and interfaces
derive from immutable ones. That is, have an interface that defines
all the non-mutating methods, and then derive a sub-interface which
adds mutating methods. That the current java design forces us to do
the opposite creates all kinds of pain.

On Thu, Jun 24, 2010 at 9:40 AM, Kevin Bourrillion <kev...@google.com> wrote:

--

Tim Peierls

unread,
Jun 24, 2010, 1:58:35 PM6/24/10
to guava-...@googlegroups.com
That wouldn't solve the problem here. In fact it sharpens the focus: If someone hands you an ImmutableX (with MutableX extending ImmutableX) you can't use it in a context where true immutability is a precondition -- it might actually be a MutableX, and someone else might change it out from under you.

The only way to ensure true immutability is to control instance creation. For example, you can't subclass ImmutableSet because it doesn't have a publicly-accessible constructor; only Guava internals can make an ImmutableSet.

If you do go back in time, ask instead for true immutability to be built into the language. :-)

--tim

Craig Berry

unread,
Jun 24, 2010, 2:07:57 PM6/24/10
to guava-...@googlegroups.com
On Thu, Jun 24, 2010 at 10:58 AM, Tim Peierls <t...@peierls.net> wrote:
> If you do go back in time, ask instead for true immutability to be built
> into the language. :-)

I'll ask for both, along with quite a few other things. :)

What would "built into the language" look like, in your view?

Christian Edward Gruber

unread,
Jun 24, 2010, 2:56:29 PM6/24/10
to guava-...@googlegroups.com
This is a terminology problem, not a logical one.  In this case, the api issue is one of readability and writability ,not immutability.  Better verbiage for what was described would be.

interface Foo {
  Thing getThing();
}

interface WritableFoo extends Foo {
  void setThing(Thing f);
}

interface ImmutableFoo extends Foo {
  // guarantees immutability semantics.
}

Point being, immutable descends from a non-writable API (no contract provided for immutabie class to break), but adds immutability semantics.

If the above assumption of writing being concentric with reading is incorrect, another way to go is this:

interface ReadableFoo {
  Thing getThing();
}

interface WritableFoo {
  void setThing(Thing f);
}

ReadWriteFoo extends ReadableFoo, WritableFoo {

}

ImmutableFoo extends ReadableFoo {
  // guarantees immutability semantics.
}

I've had arguments in Google around this based on Josh Bloch's blog post about why Collections didn't go this way.  I personally don't mind the proliferation of interfaces, but some find that ugly.  I'm generally in favour of role interfaces, per the SOLID principles, yadda yadda.

But your point about immutability semantics in the langauge is spot on and I look forward to the 20% time machine project. :)

Christian.

Kevin Bourrillion

unread,
Jun 24, 2010, 3:30:43 PM6/24/10
to guava-...@googlegroups.com
On Thu, Jun 24, 2010 at 11:56 AM, Christian Edward Gruber <cgr...@google.com> wrote:

I've had arguments in Google around this based on Josh Bloch's blog post about why Collections didn't go this way.  I personally don't mind the proliferation of interfaces, but some find that ugly.

It's just important to realize that in place of the 6 interfaces Collection, List, Set, SortedSet, Map and SortedMap, we'd have 24 interfaces.  And even then, the Writable ones would still be stuck with the scourge of "optional operations" anyway.  I just don't think that most of people who are complaining that Josh didn't go this way fully realize this fact.


Christian Edward Gruber

unread,
Jun 24, 2010, 3:44:26 PM6/24/10
to guava-...@googlegroups.com
Heh.  See the arguments I have. I had this one all over a whiteboard with Kevin C. :)

Actually, I'm not convinced it's a combinatorial problem, but that's the naive design based on this.  And frankly, I'm not a strong opponent of where it went... I just have an instinctive distrust of optional methods in Java.  In Objective-C, there's a reasonable pattern that's quite common and it doesn't use function pointers/vtables for method dispatch, so it's resaonable to do if(a.respondsTo(someselector)) { a.someSelector(); }  [translated into javaish.]

Personally, I think we could have had "Sorted" as a tagging interface, since the SortedFoo interfaces don't add contract, really.  I'm not even sure I like my ReadWriteFoo example from before... I'd just have implementations taht implemented the read and write ifs.

This is, however, getting OT for this list, because Guava went the road of extending JDK collections, which DIDN'T go down the path I prefer.  So we can wrap this up or take it offline.  It's a largely academic exercise at this point.

cheers,
Christian.

Reply all
Reply to author
Forward
0 new messages