0

I implemented a function which return Map<key,Set<objects>> , when I call this function it return the sets with type Unmodifiable.

I need to do some operations to this sets, how to convert them to modifiable sets in best practice? Otherwise I got

Exception in thread "main" java.lang.UnsupportedOperationException

Thanks in advance.

AlaaKhalil
  • 21
  • 1
  • 6

4 Answers4

1

Assuming that the Map itself is mutable, you use something like

map.replaceAll((key, set) -> new HashSet<>(set));

Example:

Map<Integer,Set<Object>> map = new HashMap<>();
map.put(5, Collections.emptySet());
map.put(10, Collections.singleton("foo"));

map.replaceAll((key, set) -> new HashSet<>(set));

map.get(5).add(42);
map.get(10).add("bar");

map.entrySet().forEach(System.out::println);
5=[42]
10=[bar, foo]

Of course, you can also replace new HashSet<>(set) with new TreeSet<>(set) or generally every Set implementation type following the copy constructor convention. When you can’t use a copy constructor, you have to resort to addAll, e.g.

map.replaceAll((key, set) -> {
    TreeSet<Object> newSet = new TreeSet<>(Comparator.comparing(Object::toString));
    newSet.addAll(set);
    return newSet;
});

There’s another option. Instead of converting all values of the map, only convert the sets on demand, i.e. when you actually want to modify them and they turn out not to have the intended type:

Map<Integer,Set<Object>> map = new HashMap<>();
map.put(5, Collections.emptySet());
map.put(10, Collections.singleton("foo"));

map.computeIfPresent(5, (key,set)->set instanceof HashSet? set: new HashSet<>()).add(42);
map.computeIfPresent(10, (key,set)->set instanceof HashSet?set:new HashSet<>()).add("bar");

map.entrySet().forEach(System.out::println);
Holger
  • 285,553
  • 42
  • 434
  • 765
0

You could copy the original set to another one that can be modified.

Something like this:

Set<YourType> newSet = unmodifiableSet
                        .stream()
                        .collect(Collectors.toSet());
// Or maybe...
Set<YourTYpe> otherSet = new HashSet<>();
otherSet.addAll(unmodifiableSet);

Then, you could modify the new list without any problem, and re-assign it into the map.

Angel González
  • 150
  • 1
  • 4
  • yes that exactly what i did, but I was searching for something more practical than looping through all the map to transfer set by set. Also I can't understand why it turns to be unmodifiable, do you have any idea? – AlaaKhalil May 19 '20 at 15:14
  • 2
    To know why it turns unmodifiable, please post the code which is returning the map – Sreenath Reddy May 19 '20 at 15:21
  • Can you show me how are you returning the Map from your function? Maybe we can figure out why this is happening. – Angel González May 19 '20 at 15:22
0

Just use the copy constructor

For a HashSet:

Set<Type> modifiable = new HashSet<>(unmodifiable);

For a TreeSet:

SortedSet<Type> modifiable = new TreeSet<>(unmodifiable);

For a LinkedHashSet:

Set<Type> modifiable = new LinkedHashSet<>(unmodifiable);

If you use a fancy Set implementation without such constructor:

Set<Type> modifiable = new MyFancySet<>();
modifiable.addAll(unmodifiable);
Olivier Grégoire
  • 33,839
  • 23
  • 96
  • 137
  • I use HashSet and that's whats I did for each set in the map. But instead of going trough key by key in the map and copying the set into a new map is there a better solution? – AlaaKhalil May 19 '20 at 15:36
  • @AlaaKhalil You should definitely add your code that show how you create and populate the map. – Olivier Grégoire May 19 '20 at 16:09
0

You can do that as a one-liner with Streams:

With map being your original map (e.g. Map<Integer, ImmutableSet<Object>> map;)

Map<Integer, Set<Object>> mutableMap = map.entrySet()
                .stream()
                .collect(Collectors.toMap(Map.Entry::getKey, Sets::newHashSet));
sapisch
  • 535
  • 3
  • 14