Fun with maps and sets

While working on Apex, I discovered an interesting behavior with Maps and the keySet method.

Problem

class TestData {
	public String data;
	
	public TestData() {}
	
	public TestData(String data) {
		this.data = data;
	}
}

Map<String, TestData> dataMap = new Map<String, TestData>{
	'foo' => new TestData('foo'),
	'bar' => new TestData('bar')
};
    
Set<String> keySet = dataMap.keySet();
System.debug(keySet.size()); // 2
System.debug(dataMap.size()); // 2

keySet.remove('foo');
System.debug(keySet.size()); // 1
System.debug(dataMap.size()); // 1

This code does not behave how you would think.  If you remove an item from the keySet set it also removes it from the dataMap. What I believe to be happening here is that the keySet method is returning a reference to the key set of the dataMap.

Solution

To work around this, we simply clone the keySet.  This will give us a new instance of the set instead of the reference version.

class TestData {
	public String data;
	
	public TestData() {}
	
	public TestData(String data) {
		this.data = data;
	}
}

Map<String, TestData> dataMap = new Map<String, TestData>{
	'foo' => new TestData('foo'),
	'bar' => new TestData('bar')
};
    
Set<String> keySet = dataMap.keySet().clone();
System.debug(keySet.size()); // 2
System.debug(dataMap.size()); // 2

keySet.remove('foo');
System.debug(keySet.size()); // 1
System.debug(dataMap.size()); // 2
This entry was posted in Development, Salesforce and tagged , , , , , . Bookmark the permalink.