84

I am using a Runnable to automatically subtract 20 from a players cooldown every second, but I have no idea how to replace the value of a value as I iterate through it. How can I have it update the value of each key?

public class CoolDownTimer implements Runnable {
    @Override
    public void run() {
        for (Long l : playerCooldowns.values()) {
            l = l - 20;
            playerCooldowns.put(Key???, l);
        }
    }

}

4 Answers 4

149

Using Java 8:

map.replaceAll((k, v) -> v - 20);

Using Java 7 or older:

You can iterate over the entries and update the values as follows:

for (Map.Entry<Key, Long> entry : playerCooldowns.entrySet()) {
    entry.setValue(entry.getValue() - 20);
}
Sign up to request clarification or add additional context in comments.

8 Comments

I can't believe I didn't think of that. Thanks.
Actually there is a FindBugs-Warning for that pattern: Inefficient use of keySet iterator instead of entrySet iterator And even if it doesn't matter in most cases, performance wise, it can make huge difference in some cases. Besides, I even think the code is clear and readable in both cases. Why use and post a pattern that clearly has some disadvantages and no advantage whatsoever? (Even if the disadvantages are arguably small)
@aioobe: I can imagine that this has different effects on different map implementation, sure, but is there one where the former form of iterating is more efficient then the latter? And therefore the FindBugs-Warning would be a false-positive? But you're right, we don't have to discuss that here. You answer is still okay, I guess.
Can you explain why this works? As I recall, you are not allowed to change elements of a collection while you iterator over them using a for each loop.
You're not allowed to do structural changes such as adding or removing elements.
|
32

Well, you can't do it by iterating over the set of values in the Map (as you are doing now), because if you do that then you have no reference to the keys, and if you have no reference to the keys, then you can't update the entries in the map, because you have no way of finding out which key was associated with the value you just updated.

When working with Maps, you have two options for updates like this, iterate through each Map.Entry<K,V> in the Map, or you can iterate through the key Set. There are methods on Map to do both of these things. Personally, I would iterate through each Map.Entry<K,V>.

for (Map.Entry<String, Long> entry : playerCooldowns.entrySet()) {
    entry.setValue(entry.getValue() - 20);
}

Comments

11

Why not iterate over the Map.Entry objects ? Each Entry will give you the key and value and you don't have to perform an additional get() on the Map to get a value.

Comments

0

If you try to add value to unmodifiable map then you will get java.lang.UnsupportedOperationException. If you are using unmodifiable map then you need to do like below.

Map<String, Object> modifiableMap = new HashMap<>(unmodifiableMap);

// Update the value of a key in the new map
modifiableMap.put("new key1", "new value");

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.