简体   繁体   English

使用比较器对列表进行排序

[英]Sort a list of lists using comparator

I am looking at sorting out a list of lists (on ArrayLists), using a comparator. 我正在寻找使用比较器整理列表列表(在ArrayLists上)。 Where the order is largest first. 订单最大的地方。 All sublists will always be of the same size. 所有子列表将始终具有相同的大小。

For example, a list of 例如,列表

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

This should 这应该

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

I have something like this but only sorts by the first item in each list 我有这样的东西,但只按每个列表中的第一项排序

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)));

Which produces: 哪个产生:

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

How can I write a comparator, which sorts by the next item in the list if the item before is equal? 如何编写一个比较器,如果前面的项目相等,它将按列表中的下一项进行排序?

For example, [7, 9, 10], [7, 9, 12] should go onto compare the two 7, then the two 9 and then 10 and 12. For example, [4, 5, 6], [4, 7, 8] should go onto compare the two 4, and tthen the 4 and 7 and stop. 例如,[7,9,10],[7,9,12]应该比较两个7,然后是两个9然后是10和12.例如,[4,5,6],[4, 7,8]应该比较两个4,然后4和7然后停止。

You can define a comparator as such: 您可以这样定义比较器:

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;
};

or: 要么:

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);

Then sort: 然后排序:

list.sort(comparator);

Update : 更新

You can further generalise this by creating a custom generic function that returns a comparator ie: 您可以通过创建返回比较器的自定义泛型函数来进一步概括:

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);
}

Now you can do: 现在你可以这样做:

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 

and so forth... 等等......

Note - I am using JDK 9+ so I don't know if the type inference is good enough in versions prior to JDK 8. - 我使用的是JDK 9+,所以我不知道在JDK 8 之前的版本中类型推断是否足够好。

Since you want to sort the integers by descending order, you first create a comparator that does that, and then chain it together multiple times to create the comparator for the list: 由于您希望按降序对整数进行排序,因此首先要创建一个比较器来执行该操作,然后将其链接在一起多次以创建列表的比较器:

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]]

The sorting order that you are trying to achieve is called Lexicographical order . 您尝试实现的排序顺序称为Lexicographical order

In order to compare two lists with this order, you can simply walk through the lists, and compare the corresponding elements. 为了比较具有此顺序的两个列表,您可以简单地遍历列表,并比较相应的元素。 As soon as one comparison yields a non-zero value, this value can be returned. 只要一个比较产生非零值,就可以返回该值。 If all elements are equal, zero is returned. 如果所有元素都相等,则返回零。

So you can implement the core of this comparison with a generic method: 因此,您可以使用通用方法实现此比较的核心:

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

Based on this method, you can assemble different sorts of comparators. 基于此方法,您可以组装不同类型的比较器。 For Integer values (and other types that implement the Comparable interface), you can use the Comparator#naturalOrder comparator as the last argument. 对于Integer值(以及实现Comparable接口的其他类型),可以使用Comparator#naturalOrder比较器作为最后一个参数。 For other types, you can feed in a different comparator. 对于其他类型,您可以使用不同的比较器。 You also always have the option to create a reversed comparator from that, for example. 例如,您也可以选择创建反向比较器

Here is an example showing how such a method can be implemented and used for sorting Integer objects, String objects, or custom objects (here, a simple Person class, which is compared by their getName ). 下面是一个示例,说明如何实现此类方法并将其用于排序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;
    }
}

You may still have to think about corner cases. 你可能仍然需要考虑角落案件。 The most important ones are: 最重要的是:

  • What happens when the lists have different sizes? 当列表有不同的大小时会发生什么?
  • What happens when the lists contain null elements? 当列表包含null元素时会发生什么?

The first one is currently handled by basically first sorting the lists by their size , which may or may not be what you want. 第一个目前通过基本上按照大小排序列表来处理,这可能是也可能不是你想要的。

The latter can easily be handled by passing a Comparator#nullsFirst or Comparator#nullsLast comparator to the outer method, but you have to be aware of that. 后者可以通过将Comparator#nullsFirstComparator#nullsLast比较器传递给外部方法来轻松处理,但您必须知道这一点。

Actually you sort according to the first element of the Lists : 实际上你根据Lists的第一个元素排序:

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

Either compare all elements but note that if your List don't have the same length, you will have to handle that too. 要么比较所有元素,要注意如果你的List没有相同的长度,你也必须处理它。

Or convert the elements of the List to String s then compare this String : 或者将List的元素转换为String然后比较这个String:

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

This way has the advantage to work even with List of String of different size. 这种方式的优点是即使使用不同大小的String List可以工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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