Отсортируйте древовидную карту списка по другому списку - PullRequest
1 голос
/ 08 мая 2020

Ну, я видел другие способы, использующие список массивов. Хотя ничего похожего на это нет.

List<String> orderList = new ArrayList<>();
orderList.add("ARMOR");
orderList.add("ADIDAS");
orderList.add("NIKE");

I have my List<TreeMap<Brand,String>> brands returning this list. 
[{NIKE=Shoes},{ADIDAS=Clothing},{ARMOR=Backpacks},{NIKE=Shorts}]

Я хочу отсортировать это по предоставленному orderList, чтобы получить следующее:

[{ARMOR=Backpacks},{ADIDAS=Clothing},{NIKE=Shoes},{NIKE=Shorts}]

Но я не знаю, как это сделать. Возможно, есть другие способы изменить это как список и изменить его порядок, я не уверен.

Ответы [ 3 ]

0 голосов
/ 09 мая 2020

Вы можете использовать настроенный Comparator напрямую с помощью метода Collections.sort() из java.util.Collections API. Вот рабочий пример копирования и вставки:

public class SortByList {

    enum Brand {
        ADIDAS, ARMOR, NIKE
    }

    static class BrandMap extends TreeMap<Brand, String> {

        public BrandMap(Brand brand, String type) {
            put(brand, type);
        }

    }

    public static void main(String[] args) {

        List<String> orderList = new ArrayList<>();
        orderList.add("ARMOR");
        orderList.add("ADIDAS");
        orderList.add("NIKE");

        List<TreeMap<Brand, String>> brands = new ArrayList<>();
        brands.add(new BrandMap(Brand.NIKE, "Shoes"));
        brands.add(new BrandMap(Brand.ADIDAS, "Clothing"));
        brands.add(new BrandMap(Brand.ARMOR, "Backpacks"));
        brands.add(new BrandMap(Brand.NIKE, "Shorts"));

        brands.sort((o1, o2) -> {
            Brand key1 = o1.keySet().iterator().next();
            Brand key2 = o2.keySet().iterator().next();
            int index1 = orderList.indexOf(key1.name());
            int index2 = orderList.indexOf(key2.name());
            return Integer.compare(index1, index2);
        });

        //[{ARMOR=Backpacks},{ADIDAS=Clothing},{NIKE=Shoes},{NIKE=Shorts}]
        System.out.println(brands);

    }

}

Просто примечание: Я создал BrandMap, чтобы легко сгенерировать тестовые данные и внести их в список. Вы можете оставить это и просто взять реализацию Comparator, написанную на лямбда-выражении. Он должен работать.

Еще одно обновление: Если вы заботитесь о производительности, и ваш orderList, вероятно, со временем станет больше, использование orderList.indexOf() внутри функции сортировки не поможет . Потому что он выполняет последовательный поиск, который стоит O(N) для каждого сравнения сортировки:

brands.sort((o1, o2) -> {
    Brand key1 = o1.keySet().iterator().next();
    Brand key2 = o2.keySet().iterator().next();
    // extra iteration costs extra O(N)
    int index1 = orderList.indexOf(key1.name());
    // extra iteration costs extra O(N)
    int index2 = orderList.indexOf(key2.name());
    return Integer.compare(index1, index2);
});

Если это так, я предлагаю сохранить orderList в HashMap вместо List или array, если есть возможность. В качестве альтернативы вы можете преобразовать его за один раз и использовать свою карту перед операцией сортировки:

// this will cost O(N) only for once
Map<String, Integer> orderMap = new HashMap<>();
for (int i = 0; i < orderList.size(); i++) {
    orderMap.put(orderList.get(i), i);
}

// then sort
brands.sort((o1, o2) -> {
    Brand key1 = o1.keySet().iterator().next();
    Brand key2 = o2.keySet().iterator().next();
    // now use the map for getting indices in constant O(1) time 
    int index1 = orderMap.get(key1.name());
    int index2 = orderMap.get(key2.name());
    return Integer.compare(index1, index2);
});

Немного дополнительно: Конечно, хранение в HashMap требует дополнительных затрат. O(N) пространство, но я предполагаю, что потребление памяти в вашем случае допустимо.

Пожалуйста. Ура!

0 голосов
/ 09 мая 2020

Это можно сделать следующим образом:

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

enum Brand {
    ARMOR, ADIDAS, NIKE
}

public class Main {
    public static void main(String[] args) {

        List<String> orderList = new ArrayList<>();
        orderList.add("ARMOR");
        orderList.add("ADIDAS");
        orderList.add("NIKE");

        List<Map<Brand, String>> list = List.of(Map.of(Brand.NIKE, "Shoes"), Map.of(Brand.ADIDAS, "Clothing"),
                Map.of(Brand.ARMOR, "Backpacks"), Map.of(Brand.NIKE, "Shorts"));

        List<Map<Brand, String>> sortedList = new ArrayList<Map<Brand, String>>();

        // Sorting
        for (String s : orderList) {
            for (Map<Brand, String> map : list) {
                for (Entry<Brand, String> entry : map.entrySet()) {
                    if (s.equals(entry.getKey().toString())) {
                        sortedList.add(map);
                    }
                }
            }
        }

        // Display the sorted list
        System.out.println(sortedList);
    }
}

Вывод:

[{ARMOR=Backpacks}, {ADIDAS=Clothing}, {NIKE=Shoes}, {NIKE=Shorts}]
0 голосов
/ 08 мая 2020

Вы можете преобразовать List<String> orderList в HashMap<String, Integer> indexMap:
["ARMOR", "ADIDAS", "NIKE"] -> {"ARMOR": 0, "ADIDAS": 1, "NIKE": 2}

Map<String, Integer> indexMap = IntStream.range(0, orderList.size()) // [0, 1, 2]
         .boxed() // use int, not Integer
         .collect(Collectors.toMap(i -> orderList.get(i), i -> i)); // add index to each element

Затем, учитывая TreeMap<String, String> brands, вы можете сделать:

List<Map.Entry<String, String>> result = brands.entrySet()
      .stream()
      .filter(entry -> indexMap.contains(entry.getKey()) // Remove elements not in indexMap... and therefore also not in orderList
      .sorted((a, b) -> Integer.compare(indexMap.get(a.getKey()), indexMap(b.getKey()))) // Sort all present brands to match the order from orderList
      .collect(Collectors.toList());
...