wintermute wintermute - 5 months ago 7
Java Question

Elegant way to create a new map from old one in Java, while keeping the sorting of elements the same

Let's consider the following code:

//...

public Map<String, Integer> getFruits() throws SomeException {
QueryResult[] queryResults = queryFruits();
Map<String, Integer> fruits = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
for (QueryResult qr : queryResults) {
fruits.put(qr.getField("Name").toString(), (Integer) rec.getField("ArticleNumber"));
}
return fruits;
}

//...

public static void main(String args[]) {
App app = new App();
Map<String, Integer> originalFruits = app.getFruits();
System.out.println(originalFruits.keySet());
}


– the result of execution will be

[Apple, banana, cherry, Dragon_Fruit, Papaya ]


After that I'm calling
getApprovedFuits()
and passing
originalFruits
to it, along with
whiteListedFruitNames
:

public Map<String, Integer> getApprovedFruits(Map<String, Integer> fruits, Set<String> whiteListedFruitNames) {
Map<String, Integer> approvedFruits = new TreeMap<>(fruits);
approvedFruits.keySet().retainAll(whiteListedFruitNames);
return approvedFruits;
}

//...

public static void main(String[] args) {
App app = new App();
Map<String, Integer> originalFruits = app.getFruits();

// v

Set<String> whiteListedFruitNames = new HashSet<>(Arrays.asList("Apple",
"banana",
"cherry",
"Dragon_Fruit",
"kiwi",
"Pineapple"));

Map<String, Integer> approvedFruits = getApprovedFruits(originalFruits, whiteListedFruitNames);
System.out.println(approvedFruits.keySet());

}


– the result of the latter
println()
will look like this:

[Apple, Dragon_Fruit, banana, cherry]


– and I expected to see this:

[Apple, banana, cherry, Dragon_Fruit]


And here is my question: how to make map constructor
TreeMap<>(fruits)
respect the sorting order of the map that is passed to it? Is there an elegant way to create a new map based on the original one, with the same sorting order?

Answer

TreeMap has a constructor from a SortedMap that retains the same Comparator (and thus, the ordering). However, since you're passing your TreeMap as a Map, this constructor is not used - instead, the constructor from a Map is called, and the ordering is lost.

To make a long story short - change getApprovedFruits' signature to use a SortedMap and you should be fine:

public Map<String, Integer> getApprovedFruits
    (SortedMap<String, Integer> fruits, Set<String> whiteListedFruitNames) {