Java - Why does Map.put() overwrite while Set.add() does not?

23,549

Solution 1

The Map behavior allows changing the values associated with equivalent keys. That is a pretty common use case: a : b becomes a : c.

Yes, over-writing Set contents with add could change something (reference value) - but that seems like a pretty narrow use case (which can be accomplished anyways - always try to remove before adding: s.remove(o); s.add(o);) relative to what one would be getting in most cases - nothing for cycles.

edit:

the one potential use I could see for that behavior, is having a constrained memory budget, lots of heavy-but-equivalent objects being created, and having references to different equal versions in various places, preventing garbage collection of the duplicate ones. Having run into that problem before, however, I don't think this behavior is even the best way to solve it.

Solution 2

In my opinion, there is no point in overwriting something in Set, since nothing will change.

However when you update a map, the key might be the same, but the value might be different.

Solution 3

Note that Map isn't actually so different... it may always change the value, but (at least in Sun's implementations) the key will remain the same even if later calls to put() use a different instance that compares as equal to the original.

Share:
23,549

Related videos on Youtube

Chris Dutrow
Author by

Chris Dutrow

Creator, EnterpriseJazz.com Owner, SharpDetail.com LinkedIn

Updated on July 09, 2022

Comments

  • Chris Dutrow
    Chris Dutrow almost 2 years

    I am wondering what the rationale is behind having Java's Map.put(key, value) method overwrite equivalently key'd values that are already in the collection, while Set.add(value) does not overwrite a pre-existing equivalent value that is already in the collection?

    Edit:

    It looks like majority viewpoint is that objects in a set that evaluate to equality should be equal in every respect, thus it shouldn't matter if Set.add(Object) overwrites equivalently valued objects or not. If two objects evaluate to equality, but do in fact hold different data, then a Map-type collection is a more appropriate container.

    I somewhat disagree with this veiwpoint.
    Example: A set holding a group of "Person" objects. In order to update some information about that person, you might want to pass the set a new, updated, person object to overwrite the old, outdated person object. In this case, a Person would hold a primary key that identifies that individual and the set would identify and compare people based only on their primary keys. This primary key is part of the person's identity as opposed to an external reference such as a Map would imply.

    • Bill K
      Bill K almost 14 years
      Why would Set.add(value) override? If this was in any way a desired behavior, you have implemented equals/hash wrong since two objects should only be equal if they have all the same attributes (in which case it's irrelevant which one is in the set). Put must override the value however, otherwise how do you get a different value for a given key?
    • Chris Dutrow
      Chris Dutrow almost 14 years
      I talked a little below about how you could override .equals() and .hashcode() to only look at the object's primary key (if it has one). However the prevailing argument on this thread seems to be that this type of data would be better represented in a Map-type collection.
    • Ar5hv1r
      Ar5hv1r over 9 years
      This isn't a "viewpoint" you get to "disagree with" - it's java.util.Set's documented behavior. You can certainly create your own data structure that does what you describe, but if you expect existing code to behave how you want, rather than how it's designed to behave, you're going to have a bad time.
  • Chris Dutrow
    Chris Dutrow almost 14 years
    Depending on how you implement set, it would change. In my case, I was using key'd objects. I implemented comparable and overrode .equals() and .hashcode() so that the set only looked at the primary keys on the object. When adding an element in the set, I wanted it to overwrite any object that already had that key with the object that held the new information. I was surprised when this didn't happen.
  • Michael Brewer-Davis
    Michael Brewer-Davis almost 14 years
    Sounds like you really have a map: primaryKey -> object
  • Chris Dutrow
    Chris Dutrow almost 14 years
    I used at map at first. The issue I had with that is that I was duplicating the keys in the map and the object which provided an opportunity for data incongruency. Its possible that I could get away with taking the key out of the object and only having it in the map, but that may cause problems in other places.
  • 3218801
    3218801 almost 14 years
    @DutrowLLC - you shouldn't be able to have duplicate keys in a map, that defeats the purpose of the map.
  • aioobe
    aioobe almost 14 years
    @DutrowLLC, nothing prevents you from doing map.put(object.key, object). Just make sure the key doesn't change (as your map may loose track of it if it changes hash-code for instance).
  • Carl
    Carl almost 14 years
    @DutrowLLC - you should be able to take the key out of the object, unless sometimes you need to get the key from the object. In which case, you should consider a BiMap - good implementations in Guava aka Google Collections and Apache Commons Collections.
  • Chris Dutrow
    Chris Dutrow almost 14 years
    @HeavyBites - I don't have duplicate keys. If you do Map.put(Object.key, Object), the key is two places: It is in the object as well as the key in the map.
  • President James K. Polk
    President James K. Polk almost 14 years
    Have you actually read the javadocs for java.util.Set, and particular for the add method?
  • emory
    emory almost 14 years
    The javadocs for Set clearly suggest that it is intended to model the mathematical concept of set. What it actually does is specified in implementation classes. The compiler does not enforce javadocs comments.
  • President James K. Polk
    President James K. Polk almost 14 years
    If you are familiar at all with Java you know that the language does not have the expressive power to provide enforcement for all the contractual requirements of an interface. Nevertheless, you may assume that all classes that implement Set (or List or whatever) obey the contract. All Sun classes do, and if you implement Set and don't your code will actually catch fire and burn you.