5

The java.util.Collections class allows me to make collection instances unmodifiable. The following method

protected Map<String, List<String>> getCacheData() {
    return Collections.unmodifiableMap(tableColumnCache);
}

returns an umodifiable Map, so that an UnsupportedOperationException is caused by attempting to alter the map instance.

@Test(expected = UnsupportedOperationException.class)
public void checkGetCacheData_Unmodifiable() {
    Map<String, List<String>> cacheData = cache.getCacheData();
    cacheData.remove("SomeItem");
}

Unfortunately all List<String> childs aren't unmodifiable. So I want to know whether there is a way to enforce the unmofiable behavior for the child values List<String> too?

Of course, alternatively I can iterate through the maps key value pairs, make the lists unmodifiable and reassemble the map.

My-Name-Is
  • 4,814
  • 10
  • 44
  • 84
  • 1
    Use final access modifier – Ashok kumar Sep 30 '13 at 13:47
  • Use unmodifiable lists as the values in your map http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Collections.html#unmodifiableList(java.util.List) – toniedzwiedz Sep 30 '13 at 13:48
  • 7
    @Ashokkuttuva that's incorrect, final stops you from creating a new instance of the object, you can still modify the contents of a list if it's made final. – David Sep 30 '13 at 13:48
  • 2
    @Ashokkuttuva final ---> Assignment. Not the content. Prefer to read http://stackoverflow.com/questions/19049697/why-final-instance-class-variable-in-java – Suresh Atta Sep 30 '13 at 13:49
  • 2
    @OP not possible with default one I guess. Write one on your own ,As per your needs. http://stackoverflow.com/questions/16356232/unmodifiable-list-in-java – Suresh Atta Sep 30 '13 at 13:51
  • 1
    The map has no influence on how the lists deal with their data, so indeed, the only thing you can do is create an unmodifiable map of unmodifiable lists. – Vincent van der Weele Sep 30 '13 at 13:51
  • @Tom This would be the alternative approach. But I don't want to explicitely make each `List` child of the `Map` unmodifiable. What I'm searching for is a framework functionality that recursively makes the map and it's child Collections unmodifiable. – My-Name-Is Sep 30 '13 at 13:52
  • @My-Name-Is It appears that the solution to your question doesn't solve your problem. So your question is probably not accurate. Why is subclassing Map to modify it to return unmodifiable lists not sufficient? Maybe try to rethink your question. – codethulhu Sep 30 '13 at 14:00

4 Answers4

5

You need an ImmutableMap full of ImmutableList instances. Use Guava to preserve your sanity.

To start off, create ImmutableList instances that you require:

ImmutableList<String> list1 = ImmutableList.of(string1, string2, etc);
ImmutableList<String> list2 = ImmutableList.of(string1, string2, etc);

Then create your ImmutableMap:

ImmutableMap<String,List<String>> map = ImmutableMap.of("list1", list1, "list2", list2);

There are numerous ways to instantiate your immutable lists and maps, including builder patterns. Check out the JavaDocs for ImmutableList and ImmutableMap for more details.

Jason Nichols
  • 11,603
  • 5
  • 34
  • 53
4

Unfortunately you will need to wrap each List as an unmodifiable List. This can be done by using java.util.Collections.unmodifiableList(...). If you have control over the creation of the Lists, then you can (and should) do this immediately after you set your values. If you don't have control over the creation of these collections, then you may want to rethink making it unmodifiable because some other code you are unaware of may be relying on that behavior. Hope that helps.

babernathy
  • 803
  • 2
  • 8
  • 23
2

Maybe that's not exactly what you need but I recommend to use Guava immutable list multimap

Nailgun
  • 3,999
  • 4
  • 31
  • 46
0

Subclass Map to return unmodifiable lists.

codethulhu
  • 3,976
  • 2
  • 21
  • 15
  • Why do that when you can use ready made ImmutableList and ImmutableMap instances? – Jason Nichols Sep 30 '13 at 14:41
  • He is creating a map with modifiable lists. When he pulls from the map, he wants to ensure that the lists are unmodifiable. The original lists are modifiable. – codethulhu Sep 30 '13 at 14:43
  • Right, and by using Guava's ImmutableMap and Immutable List the entire structure is unmodifiable – Jason Nichols Sep 30 '13 at 14:45
  • 1
    I like this solution, since I dont't rely on third party libs. In case that the solution can easily be done with few lines of additional source I prefere this solution. `private class CacheMap extends Hashtable> { @Override public List get(Object key) { List cacheData = super.get(key); return Collections.unmodifiableList(cacheData); } }` – My-Name-Is Sep 30 '13 at 14:50
  • 1
    I don't know why he doesn't want to use Guava. Maybe he doesn't want to be dependent on a third party library and all of it's transient dependencies to solve a simple problem he can manage himself. This is the solution he decided to go with. It could also be asked, "Why use Guava when Java provides unmodifiable list in the API?" – codethulhu Sep 30 '13 at 14:50
  • 1
    @My-Name-Is: The other methods on the map will still return the original lists, for example `iterator()`, and the `values()` view. – Christoffer Hammarström Oct 02 '13 at 08:10
  • @ChristofferHammarström Thanks, you are right. I now updated the implementation to: `private class ImmutableCacheMap extends Hashtable> { protected ImmutableCacheMap(Map> mutableCache) { Map> tmp = new Hashtable>(); Iterator iter = mutableCache.keySet().iterator(); while (iter.hasNext()) { String key = (String) iter.next(); List value = mutableCache.get(key); tmp.put(key, Collections.unmodifiableList(value)); } super.putAll(Collections.unmodifiableMap(tmp)); } }` – My-Name-Is Oct 02 '13 at 10:21