学习如何使用Comparator、Comparable和新的lambda表达式对Java中的原始类型和自定义对象的集合、列表和映射进行排序。我们将学习如何升序和降序排序。

//对数组进行排序 Arrays.sort( arrayOfItems ); Arrays.sort( arrayOfItems, Collections.reverseOrder() ); Arrays.sort(arrayOfItems, 2, 6); Arrays.parallelSort(arrayOfItems); //对List进行排序 Collections.sort(numbersList); Collections.sort(numbersList, Collections.reverseOrder()); //对Set进行排序 Set to List -> Sort -> List to Set Collections.sort(numbersList); //对Map进行排序 TreeMap<Integer, String> treeMap = new TreeMap<>(map); unsortedeMap.entrySet() .stream() .sorted(Map.Entry.comparingByValue()) .forEachOrdered(x -> sortedMap.put(x.getKey(), x.getValue())); //Java 8 Lambda表达式 Comparator<Employee> nameSorter = (a, b) -> a.getName().compareToIgnoreCase(b.getName()); Collections.sort(list, nameSorter); Collections.sort(list, Comparator.comparing(Employee::getName)); //按照分组排序 Collections.sort(list, Comparator .comparing(Employee::getName) .thenComparing(Employee::getDob)); 

1.对象List的排序

要对对象List进行排序,有两种常用方法,即Comparable和Comparator接口。在接下来的示例中,我们将以不同方式对Employee对象的集合进行排序。

public class Employee implements Comparable<Employee> { private Long id; private String name; private LocalDate dob; } 

1.1 Comparable接口

Comparable接口使实现它的类自然排序。这使得这些类可以与它们的其他实例进行比较。

实现Comparable接口的类必须重写compareTo()方法,在其中可以指定相同类的两个实例之间的比较逻辑。

  • 实现Comparable接口的对象可以在使用Collections.sort()和Arrays.sort() API时自动排序。
  • 实现此接口的对象将在放入排序映射(作为键)或排序集(作为元素)时自动排序。
  • 强烈建议(尽管不是必需的)自然排序与equals()方法一致。几乎所有实现Comparable的Java核心类都具有与equals()一致的自然排序。
//自然排序 ArrayList<Employee> list = new ArrayList<>(); //添加employees.. Collections.sort(list); 

要以相反的顺序对列表进行排序,最好的方法是使用Comparator.reversed() API来强制反向排序。

// 逆排序 ArrayList<Employee> list = new ArrayList<>(); //添加employees.. Collections.sort(list, Comparator.reversed()); 

1.2 Comparator接口

当不寻求自然排序时,我们可以利用Comparator接口来应用自定义排序行为。

Comparator不需要修改类的源代码。我们可以在实现Comparator接口的单独类中创建比较逻辑,并重写其compare()方法。

在排序时,我们将此比较器的实例与自定义对象列表一起传递给sort()方法。

例如,我们想按员工的名字对员工列表进行排序,而自然排序已经通过id字段实现。因此,要根据名称字段进行排序,我们必须使用Comparator接口编写自定义排序逻辑。

public class NameSorter implements Comparator&lt;Employee&gt; { @Override public int compare(Employee e1, Employee e2) { return e1.getName().compareToIgnoreCase( e2.getName() ); } } 

请注意,在给定示例中在sort()方法中使用NameSorter作为第二个参数。

ArrayList<Employee> list = new ArrayList<>(); //添加employees Collections.sort(list, new NameSorter()); 

要进行反向排序,我们只需要在Comparator实例上调用reversed()方法。

ArrayList<Employee> list = new ArrayList<>(); //添加employees Collections.sort(list, new NameSorter().reversed()); 

1.3 使用Lambda表达式进行排序

Lambda表达式有助于即时编写比较器实现。我们不需要创建一个单独的类来提供一次性的比较逻辑。

Comparator<Employee> nameSorter = (a, b) -> a.getName().compareToIgnoreCase(b.getName()); Collections.sort(list, nameSorter); 

1.4 按组排序

要在不同字段上对对象集合应用SQL样式排序(分组排序),可以使用多个比较器链。可以使用Comparator.comparing()和Comparator.thenComparing()方法创建这些比较器链。

例如,我们可以按姓名对员工列表进行排序,然后再按年龄进行排序。

ArrayList<Employee> list = new ArrayList<>(); Collections.sort(list, Comparator .comparing(Employee::getName) .thenComparing(Employee::getDob)); 

2. 对数组进行排序

使用java.util.Arrays.sort()方法以各种方式对给定的数组进行排序。sort()是一个重载的方法,可以接受各种类型的方法参数。

此方法实现了一种双轴快速排序算法,对所有数据集提供O(n log(n))性能,通常比传统的(单轴)快速排序实现更快。

2.1 升序排列

使用Arrays.sort()方法以升序对整数数组进行排序的Java程序。

//未排序 array Integer[] numbers = new Integer[] { 15, 11, ... }; //排序 array Arrays.sort(numbers); 

2.2 降序排列

Java提供了Collections.reverseOrder()比较器,以在一行中反转默认的排序行为。我们可以使用此比较器以降序对数组进行排序。请注意,数组中的所有元素必须由指定的比较器相互比较。

//未排序array Integer[] numbers = new Integer[] { 15, 11, ... }; //降序排序 array Arrays.sort(numbers, Collections.reverseOrder()); 

2.3 对数组范围进行排序

Arrays.sort()方法是一个重载的方法,还接受两个额外的参数,即fromIndex(包含)和toIndex(不包括)。

提供了上述参数后,数组将在从fromIndex位置到toIndex位置的提供范围内排序。

下面是一个示例,将数组从元素9到18进行排序。即{9, 55, 47, 18}将被排序,其余元素将不受影响。

//未排序array Integer[] numbers = new Integer[] { 15, 11, 9, 55, 47, 18, 1123, 520, 366, 420 }; //排序 array Arrays.sort(numbers, 2, 6); //打印 System.out.println(Arrays.toString(numbers)); // [15, 11, 9, 18, 47, 55, 1123, 520, 366, 420] 

2.4 并行排序

Java 8引入了许多用于并行处理数据集和流的新API。其中一个API是Arrays.parallelSort()。

parallelSort()方法将数组分成多个子数组,每个子数组在不同的线程中使用Arrays.sort()进行排序。最后,所有排序后的子数组将合并成一个排序后的数组。

parallelSort()和sort()这两个API的输出最终是相同的。只是利用Java并发的问题。

//并行排序完整数组 Arrays.parallelSort(numbers); //并行排序数组范围 Arrays.parallelSort(numbers, 2, 6); //以逆序方式并行排序数组 Arrays.parallelSort(numbers, Collections.reverseOrder()); 

3.对Set进行排序

在Java中,没有直接支持对Set进行排序。要对Set进行排序,请按照以下步骤操作:

  • 将Set转换为List。
  • 使用Collections.sort() API对List进行排序。
  • 将List转换回Set。
//未排序set HashSet<Integer> numbersSet = new LinkedHashSet<>(); //with Set items List<Integer> numbersList = new ArrayList<Integer>(numbersSet) ; //set -> list //排序 list Collections.sort(numbersList); //排序set numbersSet = new LinkedHashSet<>(numbersList); //list -> set 

4.对map进行排序

Map是键值对的集合。因此,从逻辑上讲,我们可以按键或按值对Map进行排序。

4.1 按键排序

通过将所有Map条目添加到TreeMap对象中,可以最好且最有效地按键对Map进行排序。TreeMap始终按排序顺序存储键。

 //未排序Map HashMap<Integer, String> map = new HashMap<>(); //根据key排序map TreeMap<Integer, String> treeMap = new TreeMap<>(map); 

4.2 按值排序

在Java 8中,Map.Entry类具有用于帮助我们按值对Map进行排序的静态方法comparingByValue()。

comparingByValue()方法返回一个比较器,按值上的Map.Entry自然排序。

HashMap<Integer, String> unSortedMap = new HashMap<>(); //Unsorted Map //LinkedHashMap保持元素插入的顺序。 LinkedHashMap<Integer, String> sortedMap = new LinkedHashMap<>(); unSortedMap.entrySet() .stream() .sorted(Map.Entry.comparingByValue()) .forEachOrdered(x -> sortedMap.put(x.getKey(), x.getValue())); 

5.总结

在上述示例中,我们学习了如何对数组、列表、映射和集合进行排序。

我们看到了不同初始化和使用Comparator接口的方法,包括lambda表达式。我们还学会了如何有效地使用Comparator接口。