簡體   English   中英

使用比較器對列表進行排序

[英]Sort a list of lists using comparator

我正在尋找使用比較器整理列表列表(在ArrayLists上)。 訂單最大的地方。 所有子列表將始終具有相同的大小。

例如,列表

[[4,5,6], [7,9,10], [4,7,8], [1,2,3], [7,9,12]]

這應該

[[7,9,12], [7,9,10], [4,7,8], [4,5,6], [1,2,3]]

我有這樣的東西,但只按每個列表中的第一項排序

List<List<Integer>> list = Arrays.asList(
                Arrays.asList(4,5,6),
                Arrays.asList(7,9,10), 
                Arrays.asList(4,7,8),
                Arrays.asList(1,2,3), 
                Arrays.asList(7,9,12));

list.sort((l1, l2) -> l2.get(0).compareTo(l1.get(0)));

哪個產生:

[[7, 9, 10], [7, 9, 12], [4, 5, 6], [4, 7, 8], [1, 2, 3]]

如何編寫一個比較器,如果前面的項目相等,它將按列表中的下一項進行排序?

例如,[7,9,10],[7,9,12]應該比較兩個7,然后是兩個9然后是10和12.例如,[4,5,6],[4, 7,8]應該比較兩個4,然后4和7然后停止。

您可以這樣定義比較器:

Comparator<List<Integer>> comparator = (list1, list2) -> {
       for (int i = 0; i < list1.size(); i++) {
            int value = Integer.compare(list2.get(i), list1.get(i));
            if (value != 0)
                return value;
       }
       return 0;
};

要么:

Comparator<List<Integer>> comparator = (list1, list2) -> 
IntStream.range(0, list1.size())
         .map(i -> Integer.compare(list2.get(i), list1.get(i)))
         .filter(value -> value != 0)
         .findFirst()
         .orElse(0);

然后排序:

list.sort(comparator);

更新

您可以通過創建返回比較器的自定義泛型函數來進一步概括:

static <T extends Comparable<T>> Comparator<List<T>> comparator(){
       return (o1, o2) -> IntStream.range(0, o1.size())
                                   .map(i -> o2.get(i).compareTo(o1.get(i)))
                                   .filter(value -> value != 0)
                                   .findFirst()
                                   .orElse(0);
}

現在你可以這樣做:

List<List<Integer>> integerList = Arrays.asList(
                Arrays.asList(4,5,6),
                Arrays.asList(7,9,10),
                Arrays.asList(4,7,8),
                Arrays.asList(1,2,3),
                Arrays.asList(7,9,12));

integerList.sort(comparator()); // sort list of integers descending

List<List<String>> stringList = Arrays.asList(
                Arrays.asList("a","b","c"),
                Arrays.asList("d","e","f"),
                Arrays.asList("g","h","i"));

stringList.sort(comparator()); // sort list of strings descending 

等等......

- 我使用的是JDK 9+,所以我不知道在JDK 8 之前的版本中類型推斷是否足夠好。

由於您希望按降序對整數進行排序,因此首先要創建一個比較器來執行該操作,然后將其鏈接在一起多次以創建列表的比較器:

Comparator<Integer> intReversed = Integer::compare;
intReversed = intReversed.reversed(); // integer reversed order comparator

// Chain comparators for 3 indices (could also do this in a loop for lists of unknown size)
// Each step compares the extracted element using the given comparator:
Comparator<List<Integer>> comp = Comparator.comparing(l -> l.get(0), intReversed);
comp = comp.thenComparing(l -> l.get(1), intReversed);
comp = comp.thenComparing(l -> l.get(2), intReversed);

list.sort(comp);        
System.out.println(list); // [[7, 9, 12], [7, 9, 10], [4, 7, 8], [4, 5, 6], [1, 2, 3]]

您嘗試實現的排序順序稱為Lexicographical order

為了比較具有此順序的兩個列表,您可以簡單地遍歷列表,並比較相應的元素。 只要一個比較產生非零值,就可以返回該值。 如果所有元素都相等,則返回零。

因此,您可以使用通用方法實現此比較的核心:

private static <T> int compareLexicographically(
    List<? extends T> list0,
    List<? extends T> list1, 
    Comparator<? super T> comparator)

基於此方法,您可以組裝不同類型的比較器。 對於Integer值(以及實現Comparable接口的其他類型),可以使用Comparator#naturalOrder比較器作為最后一個參數。 對於其他類型,您可以使用不同的比較器。 例如,您也可以選擇創建反向比較器

下面是一個示例,說明如何實現此類方法並將其用於排序Integer對象, String對象或自定義對象(此處為簡單的Person類,通過其getName進行比較)。

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

public class LexicographicalComparator
{
    public static void main(String[] args)
    {
        exampleWithIntegers();
        exampleWithStrings();
        exampleWithPersons();
    }

    private static void exampleWithIntegers()
    {
        List<List<Integer>> list = Arrays.asList(
            Arrays.asList(4, 5, 6), 
            Arrays.asList(7, 9, 10),
            Arrays.asList(4, 7, 8), 
            Arrays.asList(1, 2, 3),
            Arrays.asList(7, 9, 12));

        Comparator<List<Integer>> comparator = lexicographicalComparator();
        list.sort(comparator.reversed());

        System.out.println("Integers, descending:");
        list.forEach(System.out::println);
    }

    private static void exampleWithStrings()
    {
        List<List<String>> list = Arrays.asList(
            Arrays.asList("B", "B", "C"), 
            Arrays.asList("C", "B", "B"),
            Arrays.asList("B", "C", "A"), 
            Arrays.asList("A", "C", "B"),
            Arrays.asList("C", "B", "A"));

        Comparator<List<String>> comparator = lexicographicalComparator();
        list.sort(comparator);

        System.out.println("Strings, ascending:");
        list.forEach(System.out::println);
    }

    private static void exampleWithPersons()
    {
        class Person 
        {
            String name;
            Person(String name)
            {
                this.name = name;
            }
            String getName()
            {
                return name;
            }

            @Override
            public java.lang.String toString()
            {
                return name;
            }
        }

        List<List<Person>> list = Arrays.asList(
            Arrays.asList(new Person("B"), new Person("B"), new Person("C")), 
            Arrays.asList(new Person("C"), new Person("B"), new Person("B")),
            Arrays.asList(new Person("B"), new Person("C"), new Person("A")), 
            Arrays.asList(new Person("A"), new Person("C"), new Person("B")),
            Arrays.asList(new Person("C"), new Person("B"), new Person("A")));

        Comparator<List<Person>> comparator = 
            lexicographicalComparator(Comparator.comparing(Person::getName));
        list.sort(comparator);

        System.out.println("Persons, by name, ascending:");
        list.forEach(System.out::println);
    }



    private static <T extends Comparable<? super T>> Comparator<List<T>> 
        lexicographicalComparator()
    {
        return (list0, list1) -> 
            compareLexicographically(list0, list1, Comparator.naturalOrder());
    }

    private static <T> Comparator<List<T>> lexicographicalComparator(
        Comparator<? super T> comparator)
    {
        return (list0, list1) -> 
            compareLexicographically(list0, list1, comparator);
    }

    private static <T> int compareLexicographically(
        List<? extends T> list0,
        List<? extends T> list1, 
        Comparator<? super T> comparator)
    {
        if (list0.size() < list1.size())
        {
            return -1;
        }
        if (list0.size() > list1.size())
        {
            return 1;
        }
        for (int i = 0; i < list0.size(); i++)
        {
            T t0 = list0.get(i);
            T t1 = list1.get(i);
            int value = comparator.compare(t0, t1);
            if (value != 0)
            {
                return value;
            }
        }
        return 0;
    }
}

你可能仍然需要考慮角落案件。 最重要的是:

  • 當列表有不同的大小時會發生什么?
  • 當列表包含null元素時會發生什么?

第一個目前通過基本上按照大小排序列表來處理,這可能是也可能不是你想要的。

后者可以通過將Comparator#nullsFirstComparator#nullsLast比較器傳遞給外部方法來輕松處理,但您必須知道這一點。

實際上你根據Lists的第一個元素排序:

list.sort((l1, l2) -> l2.get(0).compareTo(l1.get(0)));

要么比較所有元素,要注意如果你的List沒有相同的長度,你也必須處理它。

或者將List的元素轉換為String然后比較這個String:

list.sort((l1, l2) -> l2.toString()
                        .compareTo(l1.toString()));

這種方式的優點是即使使用不同大小的String List可以工作。

暫無
暫無

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

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