如何在 Java 中对数组,列表,映射和集合进行排序
学习使用 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 中的集合进行排序。 要对集合进行排序,请按照下列步骤操作:
- 将集转换为列表。
- 使用
Collections.sort()
API 排序列表。 - 将列表转换回集合。
//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. 对自定义对象进行排序
自定义对象是用户定义的类,它们保存域数据,例如 Employee
,Department
,Account
等。
为了对自定义对象列表进行排序,我们有两种流行的方法,即Comparable
和Comparator
。 在给定的示例中,我们将对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 排序的看法。
学习愉快!