lazyloader
lazyloader

Reputation: 29

Hashset being passed as mutable copy

I am trying to use the set as a buffer to hold some values, store them in the map, and then reset itself to prepare for the next stream of data. Here is some relevant code :

Map<Set<String>, Integer> map = new LinkedHashMap<Set<String>, Integer>();
        Set<String> set = new LinkedHashSet<String>();

        set.add("A");
        map.put(set, 1);

        for (Set<String> entry : map.keySet()) {
            for (String word : entry) {
                System.out.println("BEFORE CLEAR - "+word);
            }
        }

        set.clear();

        for (Set<String> entry : map.keySet()) {
            System.out.println("AFTER CLEAR - "+entry.size());
            for (String word : entry) {
                System.out.println("AFTER CLEAR - "+word);
            }
        }

The output I get is :

BEFORE CLEAR - A
AFTER CLEAR - 0

If I change the set.clear() to set = null , then output is :

BEFORE CLEAR - A
AFTER CLEAR - 1
AFTER CLEAR - A

This implies when I clear data in set AFTER putting it into map, map does not receive a new copy - and that's why on clearing the set, the copy in map is also being cleared. I am kind of surprised by the behavior because as Java developers we are pushed towards using immutable copies, which seems to be violated here. Am I misunderstanding something ?

My question is that is there a better solution to this problem ? It works on setting set to null, which I have to re-initalize everytime with a new LinkedHashSet() in a while loop. And this looks bad to me.

EDIT : I have no problems setting set to null, I certainly want that data in the map. I only have a problem with creating new objects of set, rather than using the same one.

Upvotes: 0

Views: 258

Answers (2)

enterbios
enterbios

Reputation: 1755

If you make set = null you will erase a local reference to a set, but your map will still hold a reference to set object. To remove set from map you need to map.remove(set) Java is not doing a deep set copy while passing it as a parameter, the only copied thing is a reference to object. If you want your set to hold all data then just not clear it - create new set when you need another empty one. Think about object in java like about occupied piece of memory, when you write something like

Set<String> set = new LinkedHashSet<String>();

the first part Set<String> set is a reference (something like pointer in C) to that piece of memory, the second part new LinkedHashSet<String>(); is actually allocation these piece of memory. If you clear stuff in that piece of memory (set.clear()) you will erase stuff written there. If you need new piece of memory to write new data without erasing old one you need to call new LinkedHashSet<String>(); again.

Upvotes: 2

ccjmne
ccjmne

Reputation: 9628

First, you're right, and that's a hassle sometime but Collections in Java are indeed mutable.

Second, I think that you actually need to copy the content of your Set to put that content into your Map, instead of putting the reference to that Set, if you want to clear it later. Thus, I would suggest you to simply replace:

map.put(set, 1);

by:

map.put(new HashSet<>(set), 1);

In my opinion, it makes perfect sense, because:

  1. that's the simplest way to copy the content or your unique temporary Set
  2. you don't have to use any other new Set to fulfill the role of the first one, you can re-use it as your intended to.

Upvotes: 0

Related Questions