Java如何对数组、List、Map或Stream进行排序

后端 潘老师 7个月前 (10-16) 169 ℃ (0) 扫码查看

学习如何使用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接口。


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/back/9706.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】