Home > database >  Java map.copyOf not using source map comparator
Java map.copyOf not using source map comparator

Time:10-12

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));
}
  • Related