In competitive programming, I was solving a given problem – given an array nums
of non-negative integers, and a target sum S
, we have to find out the number of ways we can obtain target sum from sum of given numbers (where each nums[i]
can be taken as nums[i]
or -nums[i]
.
Although I came across some solutions that mainly relied on direct access tables using array (it is given that sum of numbers cannot exceed 1000), but I tried it using HashMap to reduce the space required. My code is as follows –
public int findTargetSumWays(int[] nums, int S) { Map<Integer, Integer> dp = new HashMap(); dp.put(nums[0], 1); dp.put(-nums[0], dp.getOrDefault(-nums[0], 0) + 1); for (int i=1; i<nums.length; i++) { for (Integer sum : dp.keySet()) { dp.put(sum+nums[i], dp.getOrDefault(sum+nums[i], 0) + 1); dp.put(sum-nums[i], dp.getOrDefault(sum-nums[i], 0) + 1); } } return dp.get(S); }
But I am getting ConcurrentModificationException on running the code. I tried finding about the issue. Although I got some conceptual understanding that in iteration, view of Collections can’t be structurally modified, I am not able to figure out how to find my way around it to find a solution.
Is it that a solution using HashMap(or any dynamic data structure) is not possible? Any help is appreciated.
Advertisement
Answer
for (Integer sum : dp.keySet()) { dp.put(sum+nums[i], dp.getOrDefault(sum+nums[i], 0) + 1); // ... }
If sum + nums[i]
isn’t a key that’s already in the map, this results in a structural modification of the map: that is, you’re adding a new key/value pair.
As described in the Javadoc:
The iterators returned by all of this class’s “collection view methods” are fail-fast: if the map is structurally modified at any time after the iterator is created, in any way except through the iterator’s own remove method, the iterator will throw a ConcurrentModificationException.
(If the key were already in the map, you’d just be modifying the value associated with that key, and this would be fine).
The easiest way to get around this is to take a snapshot of the keys to iterate over, by copying the elements into e.g. a list:
for (Integer sum : new ArrayList<>(dp.keySet())) {
Now, you’re iterating the list, not the map’s keyset, so you are free to modifying the map structurally inside that loop.
Separately from the question of the exception, your put/getOrDefault
lines would be more simply written as:
// dp.put(sum+nums[i], dp.getOrDefault(sum+nums[i], 0) + 1); // becomes dp.merge(sum+nums[i], 1, Integer::sum);