简体   繁体   中英

Sorting map based on list values

I have a map like below:

Map<String, String> map1 = new HashMap<String, String>();

and the contents are:

ID_1 -> ID_2
------------
100 -> 10
200 -> 20
300 -> 30

Based on the value of ID_2 I have to query an oracle table and get a code value corresponding to each entry:

ID_1 -> ID_2 -> code
--------------------
100 -> 10 -> 8
200 -> 20 -> 2
300 -> 30 -> 9

and then I will have to get the map1 sorted in ascending way by the code value ie the result should be:

200 -> 20
100 -> 10
300 -> 30

I have thought of creating an intermediary map with <ID_1, List<ID_2,code>> as <K,V> and then sort using the code value and then get the final output.

Is there any shorter way to do so, like without using an intermediary map?

You try this code below: I used int[] array instead of List

public class Test {

    public static void main(String[] args) throws Exception {
        Map<String, int[]> map = new HashMap<>();
        map.put("100", new int[]{10, 8});
        map.put("200", new int[]{20, 2});
        map.put("300", new int[]{30, 9});

        Map<String, int[]> sortByValue = sortByValue(map);
        for (Map.Entry<String, int[]> entry : sortByValue.entrySet()) {
            System.out.println(entry.getKey() +" -> "+ entry.getValue()[0]);
        }

    }

    private static Map<String, int[]> sortByValue( Map<String, int[]> map ) {
        List<Map.Entry<String, int[]>> list = new LinkedList<>(map.entrySet());
        Collections.sort(list, (o1, o2) -> Integer.compare(o1.getValue()[1], o2.getValue()[1]));
        Map<String, int[]> result = new LinkedHashMap<>();
        for (Map.Entry<String, int[]> entry : list) {
            result.put(entry.getKey(), entry.getValue());
        }
        return result;
    }
}

And it is the result:

200 -> 20
100 -> 10
300 -> 30

Based on map1 you can build new map:

 Map<String, Pair<String, String> map2

where key is id from oracle db.

As you need to have ordering you can use TreeMap and method

Map.Entry<K,V> firstEntry();

I would express you logic as follow :

  1. Get all entries in the map
  2. Affect to each one its score (through the database call)
  3. Order the entries in a final map according to step 2

It is important to notice that few maps have ordering constraints. The base implementation that comes to mind is LinkedHashMap. Furthermore "reordering an existing map" seems like a strange idea that is not backed by any methods in the Map interface . So in a nutshell, saying you need to return a Map that has an ordering constraint seems like a bad/incomplete idea - but it is certainly doable.

I would also adivse against using a TreeMap which is a Map ordered by a Comparator because I see no constraint that your ordering values are unique. Plus, your ordering is on the values, not the keys, of the map. Such a comparator would not be straightforward at all.

So, in short, what I would do is

    LinkedHashMap<String, String> sorted = map.entrySet().stream()
        // This is your DB call
        .sorted(Comparator.comparing(entry -> getDBValue(entry)))
        // Now we have an ordered stream of key/value entries from the original map
        .collect(
            // We flush back to a Map
            Collectors.toMap(
                    // Keeping the original keys as is
                    Map.Entry::getKey, 
                    // Keeping the original values as is
                    Map.Entry::getValue, 
                    // This merge step should never be called, as keys are unique. Maybe you could assert that and fail if this is called
                    (v1, v2) -> v1,
                    // This is where you instanciate the Map that respects ordering of keys. Here, LinkedHashMap is iterable in the order of insertion, which is what we want.
                    LinkedHashMap::new
                  )
    );

With Java streams you can achieve this without using any additional collections, here is an implementation.

To maintain order have used LinkedHashMap in the collector

For simplicity I have taken one more map to hold the db values [you need to change this to get from DB]

    Map<String, String> map1 = new HashMap<String, String>();
    map1.put("100", "10");
    map1.put("200", "20");
    map1.put("300", "30");

    Map<String, String> dbmap = new HashMap<String, String>();
    dbmap.put("10", "8");
    dbmap.put("20", "2");
    dbmap.put("30", "9");

    Comparator<String> comp = (k1, k2) -> dbmap.get(map1.get(k1)).compareTo(dbmap.get(map1.get(k2)));

    Map<String, String> queryMap = map1.keySet().stream().sorted(comp)
            .collect(toMap((String key) -> key, value -> (String) map1.get(value), (u, v) -> {
                throw new IllegalStateException(String.format("Duplicate key %s", u));
            }, LinkedHashMap::new));

    System.out.println(queryMap);

Ouput

{200=20, 100=10, 300=30}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM