Is it better to return an ImmutableMap or map?

Let's say I'm writing a method that should return a Map . For example:

public Map<String, Integer> foo() { return new HashMap<String, Integer>(); } 

After thinking about this for a while, I decided that there was no reason to modify this map after it was created. So, I would like to return ImmutableMap .

 public Map<String, Integer> foo() { return ImmutableMap.of(); } 

Should I leave the return type as a shared card, or should I indicate that I am returning an ImmutableMap?

On the one hand, this is why interfaces were created; to hide implementation details.
On the other hand, if I leave it like this, other developers may miss the fact that this object is immutable. Thus, I will not achieve the main goal of immutable objects; to make the code more understandable, minimizing the number of objects that can change. Even worse, after some time, someone may try to modify this object, and this will lead to a runtime error (the compiler will not warn about this).

+81
java design-patterns immutable-collections
Jun 28 '16 at 23:30
source share
9 answers
  • If you are writing an open access API and that immutability is an important aspect of your design, I will definitely make it explicit if the method name explicitly meant that the returned map would be immutable or returning a specific type of map. Mentioning it in javadoc is not enough, in my opinion.

    Since you are apparently using a Guava implementation, I was looking at a document and this is an abstract class, so it gives you a little flexibility in a particular concrete type.

  • If you are writing an internal tool / library, it becomes much more acceptable to simply return a simple Map . People will know the insides of the code they are calling, or at least will have easy access to it.

My conclusion would be that the obvious is good, do not leave things to their own devices.

+62
Jun 28 '16 at 23:49
source share

You must have an ImmutableMap as the return type. Map contains methods that are not supported by the ImmutableMap implementation (for example, put ) and are marked with @deprecated in ImmutableMap .

Using obsolete methods will result in a compiler warning, and most IDEs will warn when people try to use obsolete methods.

This preliminary warning is preferable to having run-time exceptions as your first hint that something is wrong.

+33
Jun 29 '16 at 3:04 on
source share

On the other hand, if I leave it like this, other developers may miss the fact that this object is immutable.

You should mention this in javadocs. You know, developers read them.

Thus, I will not achieve the main goal of immutable objects; make the code more clear by minimizing the number of objects that can change. Even the worst, after some time, someone may try to change this object, and this will lead to a runtime error (the compiler will not warn about this).

No developer publishes their code unverified. And when he checks this, he gets an exception when he sees not only the reason, but also the file and line where he tried to write an immutable card.

Note that only the Map itself will be immutable, and not the objects that it contains.

+15
Jun 28 '16 at 23:37
source share

if I leave it like this, other developers may miss the fact that this object is immutable

This is true, but other developers should check their code and make sure that it is covered.

However, you have two more options for solving this problem:

  • Use javadoc

     @return a immutable map 
  • Choose a descriptive method name

     public Map<String, Integer> getImmutableMap() public Map<String, Integer> getUnmodifiableEntries() 

    For a specific use case, you might even better name the methods. For example.

     public Map<String, Integer> getUnmodifiableCountByWords() 

What else can you do ?!

You can return

  • copy

     private Map<String, Integer> myMap; public Map<String, Integer> foo() { return new HashMap<String, Integer>(myMap); } 

    This approach should be used if you expect many customers to change the map, and so far the map contains only a few entries.

  • CopyOnWriteMap

    a copy on record collections is commonly used when you need to deal with
    concurrency. But this concept will also help you in your situation, because CopyOnWriteMap creates a copy of the internal data structure in a mutative operation (for example, adds, deletes).

    In this case, you need a thin shell around your map, which delegates all method calls to the base map, with the exception of mutation operations. If you cause a mutation, it creates a copy of the base map, and all subsequent calls will be delegated to that copy.

    This approach should be used if you expect some customers to change the map.

    Unfortunately, java does not have such CopyOnWriteMap . But you can find a third-party company or sell it yourself.

Finally, you must remember that the elements on your map are subject to change.

+10
Jun 29 '16 at 7:25
source share

Definitely return ImmutableMap, the justification of which is:

  • The signature of the method (including the return type) must be self-documenting. Comments are similar to customer service: if your customers must rely on them, then your main product is defective.
  • Whether something is an interface or a class matters only when extending or implementing it. Given an instance (object), 99% of the client time code does not know and do not care if something is an interface or a class. At first, I suggested that ImmutableMap is an interface. Only after I clicked the link did I realize that it was a class.
+8
Jun 29 '16 at 3:04 on
source share

It depends on the class itself. Guava ImmutableMap not intended for immutable representation in a mutable class. If your class is immutable and has some structure, which is basically an ImmutableMap , then enter the return type ImmutableMap . However, if your class is changed, do not do this. If you have this:

 public ImmutableMap<String, Integer> foo() { return ImmutableMap.copyOf(internalMap); } 

Guava will copy the map every time. It is slow. But if internalMap already ImmutableMap , then it is completely perfect.

If you do not limit your class to returning ImmutableMap , you can instead return Collections.unmodifiableMap like this:

 public Map<String, Integer> foo() { return Collections.unmodifiableMap(internalMap); } 

Please note that this is a consistent view of the map. If the internalMap changes, a cached copy of Collections.unmodifiableMap(internalMap) will be saved. However, I still prefer it for getters.

+5
Jun 29 '16 at 15:00
source share

This does not answer the exact question, but it’s still worth considering whether to return the card at all. If the map is immutable, then the main method to be provided is based on get (key):

 public Integer fooOf(String key) { return map.get(key); } 

This greatly simplifies the API. If a card is actually required, this can be left by the API client by providing a stream of records:

 public Stream<Map.Entry<String, Integer>> foos() { map.entrySet().stream() } 

The client can then create its own fixed or variable map as needed, or add entries to its own map. If the client needs to know if a value exists, an optional option may be returned instead:

 public Optional<Integer> fooOf(String key) { return Optional.ofNullable(map.get(key)); } 
+4
Jul 05 '16 at 19:08
source share

An immutable card is a type of Card. Thus, leaving the returned type of Card in order.

To ensure that users do not modify the returned object, the method documentation may describe the characteristics of the returned object.

-one
Jun 28 '16 at 23:37
source share

This is probably a matter of opinion, but it’s better to understand how to use the interface for the map class. This interface should not explicitly indicate that it is immutable, but the message will be the same if you do not expose any of the setter methods of the parent class in the interface.

Take a look at the following article:

andy gibson

-one
Jun 29 '16 at 1:10
source share



All Articles