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

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

原文: https://howtodoinjava.com/java-sorting-guide/

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

1. 排序数组

1.1 升序排序数组

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

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

程序输出。

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

1.2 降序排序数组

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

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

程序输出:

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

1.3 排序数组范围

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

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

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

程序输出:

[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()的输出最终将相同。 这只是利用多线程的问题。

Arrays.parallelSort(numbers);

Arrays.parallelSort(numbers, 2, 6);

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

2. 排序列表

可以使用Collections.sort() API 在 Java 中对列表进行排序。 它使用修改后的归并排序,并提供有保证的nlog(n)性能。

2.1 升序排序列表

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

程序输出:

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

2.2 降序排序列表

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

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

程序输出:

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

3. 排序集合

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

  1. 将集转换为列表。
  2. 使用Collections.sort() API 排序列表。
  3. 将列表转换回集合。
//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);

程序输出:

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

4. 排序映射

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

4.1 按键排序映射

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

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

程序输出:

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

4.2 按值排序映射

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

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

程序输出:

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

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

自定义对象是用户定义的类,它们保存域数据,例如 EmployeeDepartmentAccount等。

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


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接口启用其实现的类的自然排序。 它使类与其实例具有可比性。

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

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

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

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

程序输出:

[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

很多时候,我们会遇到这样的情况,即由于遗留代码问题,我们不会寻求自然排序或类文件而无法进行编辑。 在这种情况下,我们可以利用比较器接口。

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

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

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作为第二个参数。

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

程序输出:

[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实现。 我们不需要创建单独的类来提供一次性比较逻辑。

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

Collections.sort(list, nameSorter);

5.4 分组排序

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

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

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

程序输出:

[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 排序的看法。

学习愉快!