Java - convert unmodifiable sets in a map(key, set) to modifaible

799 Views Asked by At

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.

4

There are 4 best solutions below

3
Angel González On

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.

2
Olivier Grégoire On

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);
0
sapisch On

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));
0
Holger On

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);