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

时间:2018-03-31 11:12:33

标签: java algorithm sorting oop functional-programming

我正在寻找使用比较器整理列表列表(在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并停止。

4 个答案:

答案 0 :(得分:3)

您可以这样定义比较器:

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版本中的类型推断是否足够好。

答案 1 :(得分:1)

由于您希望按降序对整数进行排序,因此首先要创建一个比较器来执行该操作,然后将其链接在一起多次以创建列表的比较器:

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

答案 2 :(得分:1)

您尝试实现的排序顺序称为Lexicographical order

为了比较具有此顺序的两个列表,您可以简单地浏览列表,并比较相应的元素。只要一个比较产生非零值,就可以返回该值。如果所有元素都相等,则返回零。

因此,您可以使用通用方法实现此比较的核心:

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

基于此方法,您可以组装不同类型的比较器。对于Integer值(以及实现Comparable接口的其他类型),可以使用Comparator#naturalOrder比较器作为最后一个参数。对于其他类型,您可以使用不同的比较器。您也可以选择从中创建reversed comparator,例如。

这是一个示例,说明如何实现这样的方法并用于排序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个元素时会发生什么?

第一个目前通过基本上按照 size 对列表进行排序来处理,这可能是您想要的,也可能不是。

后者可以通过将Comparator#nullsFirstComparator#nullsLast比较器传递给外部方法来轻松处理,但您必须注意这一点。

答案 3 :(得分:0)

实际上,您根据Lists的第一个元素进行排序:

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

要么比较所有元素,要注意如果你的List没有相同的长度,你也必须处理它。

或者将List的元素转换为String,然后比较此字符串:

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

即使List String大小不同,这种方式也有效。