Why Not Return the Stored Object
The second problem with making the storage class public is that the caller now holds the map’s own storage object, not a copy, and its fields are public. So the caller can write them, and any change it makes is a change to the map’s stored data. To get these types, the caller has to declare the map with its concrete class:
ArrayMap<String, String> accounts = new ArrayMap<>();
The caller can overwrite the key:
for (ArrayMap.Entry<String, String> e : accounts.entries()) {
if (e.key.equals("ann")) {
e.key = "bob"; // rewriting a stored key in place
}
}
The entry is still in the map, but every lookup compares keys with equals, so the entry can no longer be found under its old key, and it is found under the new one instead. No exception is thrown.
The caller can overwrite the value:
for (ArrayMap.Entry<String, String> e : accounts.entries()) {
if (e.key.equals("ann")) {
e.value = null; // stores a value that put would reject
}
}
put rejects a null value, but a direct write does not. After this line, get returns null for a key that is present, which breaks the rule that a null from get means the key is absent.
For LinkedMap, the stored object is a Node, and a Node has a next field. So the caller can also change the list’s links:
LinkedMap<String, String> logins = new LinkedMap<>();
// ...
for (LinkedMap.Node<String, String> node : logins.entries()) {
node.next = null; // cuts off the rest of the list
}
Java does not report an error. Every node after this one drops out of the list, but size still counts them, and nothing fails until some later operation gives a wrong answer.
We only want the caller to read a pair, not change the map’s internals.