簡體   English   中英

如何對 Map 進行排序<string, list<customobject> &gt;?</string,>

[英]How can I sort a Map<String, List<CustomObject>>?

我對這個問題做了很多研究,但我還沒有找到一種方法來對自定義 object 列表( Map<String, List<CustomObj>> )的 map 進行排序,基於CustomObj屬性(如SORT_BY_NAMESORT_BY_DATE ETC)。

我的問題的一個激勵例子是:

  • 我有一個自定義 object: Person (屬性為NameDateOfBith等...);
  • 我有一個Person Map的 Map List為: Map<String, List<Person>> map 密鑰是用於其他目的的String
  • 我想創建一個比較器和一種排序方法,根據Person object 的屬性(名稱、日期等)之間的比較,按升序對 map 進行排序。

為簡單起見,我報告了真實代碼,但適用於Person object 的簡化案例,因為它已經代表了實體的概念。

Person.java -> 自定義 object

public class Person {

     private String name;
     private Date dateOfBirth;
     ...

     // Empty and Full attrs Constructors
     ...

     // Getter and Setter
     ...

     // Comparator by name
     public static Comparator<Person> COMPARE_BY_NAME = Comparator.comparing(one -> one.name);
     // Comparator by date
     public static Comparator<Person> COMPARE_BY_DATE = Comparator.comparing(one -> one.dateOfBirth);

}

分揀機Sorter.java -> 分揀機 object

public class Sorter {

     // List Comparator of Person by Date 
     public static final Comparator<? super List<Person>> COMPARATOR_BY_DATE = (Comparator<List<Person>>) (p1, p2) -> {
          for (Persontab person1: p1) {
              for (Person person2: p2) {
                  return Person.COMPARE_BY_DATE.compare(person1, person2);
              }
          }
          return 0;
     };

     // List Comparator of Person by Name
     public static final Comparator<? super List<Person>> COMPARATOR_BY_NAME = (Comparator<List<Person>>) (p1, p2) -> {
          for (Persontab person1: p1) {
              for (Person person2: p2) {
                  return Person.COMPARE_BY_NAME.compare(person1, person2);
              }
          }
          return 0;
     };

     // Sorting method
     public Map<String, List<Person>> sort(Map<String, List<Person>> map, Comparator<? super List<Person>> comparator) {
          return map.entrySet()
            .stream()
            .sorted(Map.Entry.comparingByValue(comparator))
            .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v1, LinkedHashMap::new));
     }

}

Main.java -> 起始碼

public class MainApp {

     public static void main(String[] args) {

          Map<String, List<Person>> exampleMap = new HashMap<>();
          List<Person> personList = new ArrayList<>();
          personList.add(new Person("name1", new Date("2022-01-01")));              
          personList.add(new Person("name12", new Date("2022-01-05")));
          personList.add(new Person("name13", new Date("2022-01-03")));
          map.put("2022-01", personList);

          personList.clear();
          personList.add(new Person("name14", new Date("2021-02-01")));              
          personList.add(new Person("name3", new Date("2021-02-05")));
          personList.add(new Person("name4", new Date("2021-02-03")));
          map.put("2021-02", personList);

          Sorter sorter = new Sorter();

          // Example of sorting by date
          map = sorter.sort(exampleMap, Sorter.COMPARATOR_BY_DATE);
          // In this case the sorting works correctly, or rather it sorts the items by date as I expect
         
          // Example of sorting by name
          map = sorter.sort(exampleMap, Sorter.COMPARATOR_BY_NAME);
          // In this case, I don't think sorting works correctly. Sort each list of elements for each key in ascending order. But it doesn't sort the map elements.

          /* I expect to have the following map when sort by date:
             "2021-02": [
               Person("name14", new Date("2021-02-01")),
               Person("name4", new Date("2021-02-03")),
               Person("name3", new Date("2021-02-05"))
             ], 
             "2022-01": [
               Person("name14", new Date("2021-02-01")),
               Person("name13", new Date("2022-01-03")),
               Person("name12", new Date("2022-01-05"))
             ]
             
     }

}

首先,讓我們重申一下: HashMap是無序的,所以你需要別的東西。 您的Sorter.sort()方法實際上將值收集到LinkedHashMap中,它提供了基於插入順序的迭代順序,並且適用於您的用例。 只是要清楚(也為了其他人):這不會對 map 本身進行排序,而是創建一個新的LinkedHashMap

現在到你的比較器:如果你想比較 2 個列表,你可能想比較相同索引的元素。 因此,您的比較器需要是這樣的:

Comparator<List<Person>> = (l1, l2) -> {
   Iterator<Person> itr1 = l1.iterator();
   Iterator<Person> itr2 = l2.iterator();

   while( itr1.hasNext() && itr2.hasNext() ) {
     Person p1 = itr1.next();
     Person p2 = itr1.next();

     int result = Person.COMPARE_BY_DATE.compare(p1, p2);
     if( result != 0 ) {
       return result;
     }
   }

   return 0;
};
 

但是,列表也可能具有不同的長度,因此您可能也想處理它:

Comparator<List<Person>> = (l1, l2) -> {
   //iterators and loop here

   //after the loop it seems all elements at equal indices are equal too
   //now compare the sizes

   return Integer.compare(l1.size(), l2.size());
}

通過更改上面代碼中使用的Map的類型,正如@Thomas 所建議的那樣,在TreeMap<>中,我找到了問題的解決方案如下:

  1. 我首先將所有Person object 列表合並到一個列表中。 然后按所選標准對其進行排序,例如Person.COMPARE_BY_NAME
  2. 我創建了一個算法,可以根據我的項目標准在 map 中對排序列表進行重新分組。 這個 map 的鍵對應於Person object 的月 + 年的串聯。 算法在評論底部報告;
  3. 我根據所選屬性對 map 進行排序,例如Sorter.COMPARATOR_BY_NAME

Di seguito il codice è come segue:

在創建 Map 之前將所有List<Person>合並到一個 -> main 或某處

    ...
    //
    List<Person> newPersonList = new ArrayList<>();
    newPersonList.addAll(oldPersonList1);
    newPersonList.addAll(oldPersonList2);
    ...

創建 Map 之前的主要或某處

    ...
    groupList(Person.COMPARE_BY_NAME, Sorter.COMPARATOR_BY_NAME);
    ...

GroupPerson -> 將合並的List<Person>分組到TreeMap<String, List<Person>>的方法

    public Map<String, List<Person>> groupList(final Comparator<? super Person> itemComparator, final Comparator<? super List<Person>> listComparator)
         
         // Sort Person list by comparator before create TreeSet
         newPersonList.sort(itemComparator);

         Map<String, List<Person>> personMapGrouped = new TreeMap<>();
         
         // Here, create a Map of list
         for (Person person: newPersonList) {
             final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy MM", Locale.getDefault());
             final String groupKey = dateFormat.format(person.getDateOfBirth());

             if (personMapGrouped.containsKey(groupKey)) {
                // The key is already in the TreeMap; add the Person object against the existing key.
                final List<Person> personListGrouped = personMapGrouped.get(groupKey);
                if (personListGrouped!= null) {
                   personListGrouped.add(person);
                }
             } else {
                // The key is not there in the TreeMap; create a new key-value pair
                final List<Person> personListGrouped = new ArrayList<>();
                personListGrouped.add(person);
                personMapGrouped.put(groupKey, personListGrouped);
             }
         }
         // Here sort the Map by params passed
         final TabPersonSorter sorter = new TabPersonSorter();
         personMapGrouped = sorter.sort(personMapGrouped, listComparator);
    }

在這種情況下,使用上面 main 中創建的列表,獲得的結果是:

    "List<Person> mergedList": [
        Person("name1", new Date("2022-01-01")),
        Person("name3", new Date("2021-02-05")),
        Person("name4", new Date("2021-02-03")),
        Person("name12", new Date("2022-01-05")),
        Person("name13", new Date("2022-01-03")),
        Person("name14", new Date("2021-02-01"))
    ]

    "Map<String, List<Person>> mergedMap": {
        "2022-01": [
            Person("name1", new Date("2022-01-01")),
            Person("name12", new Date("2022-01-05")),
            Person("name13", new Date("2022-01-03"))
        ], 
        "2021-02": [
            Person("name3", new Date("2021-02-05")),
            Person("name4", new Date("2021-02-03"))
        ],
        "2022-02": [
            Person("name14", new Date("2021-02-01"))
        ]
    } 

顯然,如果 map 中的分組不受諸如只有年 + 月這樣的限制性日期的約束,那么排序將在不同的組中產生預期的效果。 事實上,在按日期排序的情況下,這一點得到了很好的尊重。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM