Reputation: 19
The code below is illustrating my problem :
public class TreeMapCopyProblem {
private static TreeMap<Integer, SortedSet<Integer>> treeMap1;
private static TreeMap<Integer, SortedSet<Integer>> treeMap2;
public static void main(String[] args) {
fillTreeMap1();
initializeTreeMap2();
test();
}
private static void initializeTreeMap2() {
treeMap2 = (TreeMap<Integer, SortedSet<Integer>>) treeMap1.clone();
}
private static void fillTreeMap1() {
treeMap1 = new TreeMap<>();
SortedSet<Integer> values0 = Stream.of(0).collect(Collectors.toCollection(TreeSet::new));
SortedSet<Integer> values1 = Stream.of(1).collect(Collectors.toCollection(TreeSet::new));
SortedSet<Integer> values2 = Stream.of(2).collect(Collectors.toCollection(TreeSet::new));
treeMap1.put(0, values0);
treeMap1.put(1, values1);
treeMap1.put(2, values2);
//Once fill, this treeMap does not have to be modified anymore
}
private static void test(){
treeMap2.get(0).add(99);
System.out.println(treeMap1);
}
}
TreeMap1 is modified too because it's a shallow Copy referring to the same set.
I had the same behaviour using
treeMap2 = new TreeMap<>(treeMap1);
or
treeMap2.putAll(treeMap1);
So... How do we simply do a non shallow copy of an existing Tree Map so we can initialize a new TreeMap from an existing one and then modify only the second one?
Upvotes: -1
Views: 92
Reputation: 181008
How do we simply do a non shallow copy of an existing Tree Map so we can initialize a new TreeMap from an existing one and then modify only the second one?
Supposing that you don't feel the need to make copies of the Integer
objects involved (which are, after all, immutable), we only need to go one level deeper to make a sufficiently deep copy of your original map. I would probably do that by streaming and then collecting the original map's entry set:
private static void initializeTreeMap2() {
treeMap2 = treeMap1.entrySet()
.stream()
.collect(TreeMap::new,
(m, e) -> m.put(e.getKey(), new TreeSet<>(e.getValue())),
(m1, m2) -> m1.putAll(m2));
}
This is similar in concept to your forEach()
-based approach, but it feels a bit cleaner to me. Among other things, there is no way for any thread to observe a partially filled state of treeMap2
. Also, this stream-based approach is auto-parallelizable, whereas forEach()
is unavoidably serial.
Upvotes: 1
Reputation: 19
private static void initializeTreeMap2() {
treeMap2 = (new TreeMap<>());
treeMap1.forEach((integer, integers) -> {
SortedSet<Integer> newSet = new TreeSet<>(integers);
treeMap2.put(integer, newSet);
});
}
This works. But is it the good approach ?
(Note putAll method also give a shallow copy.)
Upvotes: 0