# 如何在 Java 中对数组，列表，映射和集合进行排序

> 原文： [https://howtodoinjava.com/java-sorting-guide/](https://howtodoinjava.com/java-sorting-guide/)

学习使用 Java 对数组或集合进行排序。 我们将学习对包含原始类型和自定义对象的集合，列表和映射进行排序。 我们还将学习升序排序。

## 1\. 排序数组

#### 1.1 升序排序数组

Java 程序使用[`Arrays.sort()`](https://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html#sort(byte[]))方法以升序对整数数组进行排序。

```java
import java.util.Arrays;

public class JavaSortExample 
{    
    public static void main(String[] args) 
    {
        //Unsorted array
        Integer[] numbers = new Integer[] { 15, 11, 9, 55, 47, 18, 520, 1123, 366, 420 };

        //Sort the array
        Arrays.sort(numbers);

        //Print array to confirm
        System.out.println(Arrays.toString(numbers));
    }
}

```

程序输出。

```java
[9, 11, 15, 18, 47, 55, 366, 420, 520, 1123]

```

#### 1.2 降序排序数组

Java 提供`Collections.reverseOrder()`比较器，以在一行中反转默认的排序行为。 使用此命令以降序对数组进行排序。

```java
//Unsorted array
Integer[] numbers = new Integer[] { 15, 11, 9, 55, 47, 18, 520, 1123, 366, 420 };

//Sort the array in reverse order
Arrays.sort(numbers, Collections.reverseOrder());

//Print array to confirm
System.out.println(Arrays.toString(numbers));

```

程序输出：

```java
[1123, 520, 420, 366, 55, 47, 18, 15, 11, 9]

```

#### 1.3 排序数组范围

`Arrays.sort()`方法是重载方法，它采用两个附加参数，即`fromIndex`（包括）和`toIndex`（排除）。 提供时，数组将在从位置`fromIndex`到位置`toIndex`的指定范围内排序。

给定示例对元素 9 到 18 之间的数组进行排序，即{9，55，47，18}将被排序，其余元素将不会被触摸。

```java
//Unsorted array
Integer[] numbers = new Integer[] { 15, 11, 9, 55, 47, 18, 1123, 520, 366, 420 };

//Sort the array
Arrays.sort(numbers, 2, 6);

//Print array to confirm
System.out.println(Arrays.toString(numbers));

```

程序输出：

```java
[15, 11, 9, 18, 47, 55, 1123, 520, 366, 420]

```

#### 1.4 Java 8 并行排序

Java 8 引入了许多用于并行处理数据集和流的新 API。 一种此类 API 是`Arrays.parallelSort()`。 它将数组分为不同的子数组，并且每个子数组在**不同线程**中以`Arrays.sort()`进行排序。 最终，所有排序的子数组将合并为一个数组。

两个 API 的`parallelSort()`和`sort()`的输出最终将相同。 这只是利用多线程的问题。

```java
Arrays.parallelSort(numbers);

Arrays.parallelSort(numbers, 2, 6);

Arrays.parallelSort(numbers, Collections.reverseOrder());

```

## 2\. 排序列表

可以使用[`Collections.sort()`](https://docs.oracle.com/javase/7/docs/api/java/util/List.html) API 在 Java 中对列表进行排序。 它使用修改后的**归并排序**，并提供有保证的`nlog(n)`性能。

#### 2.1 升序排序列表

```java
//Unsorted list
Integer[] numbers = new Integer[] { 15, 11, 9, 55, 47, 18, 1123, 520, 366, 420 };
List<Integer> numbersList = Arrays.asList(numbers);

//Sort the list
Collections.sort(numbersList);

//Print list to confirm
System.out.println(numbersList);

```

程序输出：

```java
[9, 11, 15, 18, 47, 55, 366, 420, 520, 1123]

```

#### 2.2 降序排序列表

与数组类似，使用`Collections.reverseOrder()`可以反转默认的排序行为。

```java
//Unsorted list
Integer[] numbers = new Integer[] { 15, 11, 9, 55, 47, 18, 1123, 520, 366, 420 };
List<Integer> numbersList = Arrays.asList(numbers);

//Sort the list
Collections.sort(numbersList, Collections.reverseOrder());

//Print list to confirm
System.out.println(numbersList);

```

程序输出：

```java
[1123, 520, 420, 366, 55, 47, 18, 15, 11, 9]

```

## 3\. 排序集合

不直接支持对 Java 中的集合进行排序。 要对集合进行排序，请按照下列步骤操作：

1.  将集转换为列表。
2.  使用`Collections.sort()` API 排序列表。
3.  将列表转换回集合。

```java
//Unsorted list
HashSet<Integer> numbersSet = new LinkedHashSet<>( 
        Arrays.asList(15, 11, 9, 55, 47, 18, 1123, 520, 366, 420) );

List<Integer> numbersList = new ArrayList<Integer>(numbersSet) ;        //set -> list

//Sort the list
Collections.sort(numbersList);

numbersSet = new LinkedHashSet<>(numbersList);          //list -> set

//Print set to confirm
System.out.println(numbersSet);

```

程序输出：

```java
[9, 11, 15, 18, 47, 55, 366, 420, 520, 1123]

```

## 4\. 排序映射

映射是键值对的集合。 因此，映射可以通过两种方式进行排序，即**按键**排序或**按值**排序。

#### 4.1 按键排序映射

按键对映射进行排序的最好，最有效的方法是在`TreeMap`对象中添加所有映射项。 `TreeMap`按键对条目集进行排序。

```java
HashMap<Integer, String> map = new HashMap<>();

map.put(50, "Alex");
map.put(20, "Charles");
map.put(60, "Brian");
map.put(70, "Edwin");
map.put(120, "George");
map.put(10, "David");

TreeMap<Integer, String> treeMap = new TreeMap<>(map);

System.out.println(treeMap);

```

程序输出：

```java
{10=David, 20=Charles, 50=Alex, 60=Brian, 70=Edwin, 120=George}

```

#### 4.2 按值排序映射

在 Java 8 中，`Map.Entry`类具有静态方法`compareByValue()`，可帮助您按值排序。 此方法返回一个**比较器**，该比较器以自然顺序比较值上的`Map.Entry`。

```java
HashMap<Integer, String> unSortedMap = new HashMap<>();

unSortedMap.put(50, "Alex");
unSortedMap.put(20, "Charles");
unSortedMap.put(60, "Brian");
unSortedMap.put(70, "Edwin");
unSortedMap.put(120, "George");
unSortedMap.put(10, "David");

//LinkedHashMap preserve the ordering of elements in which they are inserted
LinkedHashMap<Integer, String> sortedMap = new LinkedHashMap<>();

unSortedMap.entrySet()
    .stream()
    .sorted(Map.Entry.comparingByValue())
    .forEachOrdered(x -> sortedMap.put(x.getKey(), x.getValue()));

System.out.println(sortedMap);

```

程序输出：

```java
{50=Alex, 60=Brian, 20=Charles, 10=David, 70=Edwin, 120=George}

```

## 5\. 对自定义对象进行排序

自定义对象是**用户定义的类**，它们保存域数据，例如 `Employee`，`Department`，`Account`等。

为了对自定义对象列表进行排序，我们有两种流行的方法，即`Comparable`和`Comparator`。 在给定的示例中，我们将对`Employee`类的实例的集合进行排序。

```java

import java.time.LocalDate;

public class Employee implements Comparable<Employee> {

    private Long id;
    private String name;
    private LocalDate dob;

    public Employee(Long id, String name, LocalDate dob) {
        super();
        this.id = id;
        this.name = name;
        this.dob = dob;
    }

    @Override
    public int compareTo(Employee o) {
        return this.getId().compareTo(o.getId());
    }

    //Getters and Setters

    @Override
    public String toString() {
        return "Employee [id=" + id + ", name=" + name + ", dob=" + dob + "]";
    }
}

```

#### 5.1 `Comparable`

[`Comparable`](https://docs.oracle.com/javase/8/docs/api/java/lang/Comparable.html)接口启用其实现的类的**自然排序**。 它使类与其实例具有可比性。

实现`Comparable`接口的类必须覆盖`compareTo()`方法，在该方法中，它可以指定同一类的两个实例之间的比较逻辑。

可以通过`Collections.sort()`和`Arrays.sort()`自动对实现此接口的对象的列表（和数组）进行排序。 当将实现此接口的对象放入已排序的映射（作为键）或已排序的集合（作为元素）时，将自动对其进行排序。

强烈建议（尽管不是必需的）自然顺序应与`equals`一致。 实际上，所有实现`Comparable`的 Java 核心类都具有与`equals`一致的自然顺序。

```java
ArrayList<Employee> list = new ArrayList<>();

list.add(new Employee(1l, "Alex", LocalDate.of(2018, Month.APRIL, 21)));
list.add(new Employee(4l, "Brian", LocalDate.of(2018, Month.APRIL, 22)));
list.add(new Employee(3l, "Piyush", LocalDate.of(2018, Month.APRIL, 25)));
list.add(new Employee(5l, "Charles", LocalDate.of(2018, Month.APRIL, 23)));
list.add(new Employee(2l, "Pawan", LocalDate.of(2018, Month.APRIL, 24)));

Collections.sort(list);

System.out.println(list);

```

程序输出：

```java
[Employee [id=1, name=Alex, dob=2018-04-21], ]
Employee [id=2, name=Pawan, dob=2018-04-24], 
Employee [id=3, name=Piyush, dob=2018-04-25], 
Employee [id=4, name=Brian, dob=2018-04-22], 
Employee [id=5, name=Charles, dob=2018-04-23]]

```

#### 5.2 `Comparator`

很多时候，我们会遇到这样的情况，即由于遗留代码问题，我们不会寻求自然排序或类文件而无法进行编辑。 在这种情况下，我们可以利用[比较器](https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html)接口。

比较器不需要修改该类的源代码。 我们可以在单独的类中创建比较逻辑，以实现`Comparator`接口并覆盖其`compare()`方法。 然后将此比较器以及自定义对象列表传递给`sort()`方法。

例如，在比较器下面，按员工列表的**名称**对其排序。

```java
import java.util.Comparator;

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

```

Java 程序使用`Comparator`接口实现对列表进行排序。 注意在`sort()`方法中使用`NameSorter`作为第二个参数。

```java
ArrayList<Employee> list = new ArrayList<>();

list.add(new Employee(1l, "Alex", LocalDate.of(2018, Month.APRIL, 21)));
list.add(new Employee(4l, "Brian", LocalDate.of(2018, Month.APRIL, 22)));
list.add(new Employee(3l, "Piyush", LocalDate.of(2018, Month.APRIL, 25)));
list.add(new Employee(5l, "Charles", LocalDate.of(2018, Month.APRIL, 23)));
list.add(new Employee(2l, "Pawan", LocalDate.of(2018, Month.APRIL, 24)));

Collections.sort(list, new NameSorter());

System.out.println(list);

```

程序输出：

```java
[Employee [id=1, name=Alex, dob=2018-04-21], 
Employee [id=4, name=Brian, dob=2018-04-22], 
Employee [id=5, name=Charles, dob=2018-04-23], 
Employee [id=2, name=Pawan, dob=2018-04-24], 
Employee [id=3, name=Piyush, dob=2018-04-25]]

```

#### 5.3 用 Java 8 Lambda 排序

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

```java
Comparator<Employee> nameSorter = (a, b) -> a.getName().compareToIgnoreCase(b.getName());

Collections.sort(list, nameSorter);

```

#### 5.4 分组排序

要使用**链中的多个比较器**对不同字段上的对象集合进行分组排序。 可以使用`Comparator.comparing()`和`Comparator.thenComparing()`方法创建比较器的链接。

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

```java
ArrayList<Employee> list = new ArrayList<>();

list.add(new Employee(1l, "Alex", LocalDate.of(2018, Month.APRIL, 21)));
list.add(new Employee(4l, "Brian", LocalDate.of(2018, Month.APRIL, 01)));
list.add(new Employee(3l, "Alex", LocalDate.of(2018, Month.APRIL, 25)));
list.add(new Employee(5l, "Charles", LocalDate.of(2018, Month.APRIL, 23)));
list.add(new Employee(2l, "Alex", LocalDate.of(2018, Month.APRIL, 30)));

Collections.sort(list, Comparator
                        .comparing(Employee::getName)
                        .thenComparing(Employee::getDob));

System.out.println(list);

```

程序输出：

```java
[Employee [id=1, name=Alex, dob=2018-04-21], 
Employee [id=3, name=Alex, dob=2018-04-25], 
Employee [id=2, name=Alex, dob=2018-04-30], 
Employee [id=4, name=Brian, dob=2018-04-01], 
Employee [id=5, name=Charles, dob=2018-04-23]]

```

## 6\. 总结

在上面给出的示例中，我们学习了对数组，列表，映射和集合进行排序。 我们看到了用于初始化和使用`Comparator`接口（包括 lambda 表达式）的不同方法。

欢迎分享您对 **Java** 排序的看法。

学习愉快！