I have innumerable maps with custom keys and comparators. I have noticed that the when I create a map using code like
var map = TreeMap<>( someCustomComparator );
And then later I create an immutable (and small and fast) copy of it using code like:
map = Map.copyOf( map );
Then map.get( similarObject )
then fails to retrieve someObject
, even when someObject
and similarObject
compare equal ("have the same equivalence class") under the comparator someCustomComparator
.
Debugging into the API I find that Map.copyOf
returns a map implementation that uses Object::equals
to compare keys, i.e. it does not use the comparator used to construct the original map (in my example this would be someCustomComparator
). Obviously when someObject
and similarObject
are not the same object but have the same equivalence class under someCustomComparator
but Object::equals
is not overridden, this then yields the bizarre result that
map.get( similarObject ) ==> someObject
before the map = Map.copyOf( map )
instruction, and
map.get( similarObject ) ==> null
after the map = Map.copyOf( map )
instruction. Is this expected behaviour I have to live with or should I report a Java defect ?
(Note that the class of some/similarObject
also implements comparable
, and that is also ignored by the Map.copyOf
implementation.)
(I presume this behaviour is common across all the collection copyOf implementations.)
CodePudding user response:
The specification for the copyOf
method states
Returns an unmodifiable Map containing the entries of the given Map.
(emphasis mine)
Which means only the entries get copied, nothing else. Hence, it is expected behavior, not a bug.
On the other hand, as suggested in the comments, the documentation for the constructor of the TreeMap
class says
Constructs a new tree map containing the same mappings and using the same ordering as the specified sorted map. This method runs in linear time.
Parameters:
m
- the sorted map whose mappings are to be placed in this map, and whose comparator is to be used to sort this map
(again, emphasis mine)
CodePudding user response:
In addition to what Federico said in his answer, the Javadoc of Map
also says this in the "Unmodifiable Maps" section:
- The iteration order of mappings is unspecified and is subject to change.
If you need an unmodifiable map that uses the same order, wrap the map using Collections.unmodifiableMap
. If you want a copy, just create a new TreeMap
as Federico said:
private static <K, V> SortedMap<K, V> copyOf(SortedMap<K, V> map) {
return Collections.unmodifiableSortedMap(new TreeMap<>(map));
}