如何在Java集合中获得最大或最小元素

222 阅读4分钟

指南

在本指南中,我们将看看_如何_通过字段_获得Java集合中的最大或最小元素_,包括原始类型和自定义可比较对象。

用Collections.max()获取最大或最小元素

Collections 框架为我们提供了各种帮助性的静态方法,用于在Java中处理集合。难怪这个框架也允许我们搜索并返回一个集合的最大或最小元素。

使用原始类型的 Collections.max() 和 Collections.min()

在大多数方面,使用原始类型是相当容易的,只要它们是Comparable ,我们就可以很容易地搜索它们。

为了找到由原始类型组成的集合的最大或最小元素,我们只需调用Collections.max()Collections.min() 方法,并传入我们要搜索的集合。

List<Integer> list = List.of(1, 5, 4, 3, 7, 6, 9, 4);
        
Integer min = Collections.min(list);
Integer max = Collections.max(list);

System.out.println(String.format("Minimum element is %s", min));
System.out.println(String.format("Maximum element is %s", max));

运行这段代码会返回我们的最大和最小元素。

Minimum element is 1
Maximum element is 9

使用自定义对象的 Collections.max() 和 Collections.min()

虽然,我们很少只用原始类型来工作。通常情况下,我们会与对象一起工作。当然,由于这些结构要复杂得多--你得决定什么是两者之间的_更大_元素。

通常,这是通过实现Comparable 接口来实现的,它允许你比较一个类的任何两个实例,以确定哪个是更大的。让我们定义一个类并使其成为Comparable

public class Book implements Comparable<Book> {
    private String author;
    private String name;
    private int pageNumber;

    // Constructor, getters and setters

    @Override
    public int compareTo(Book book) {
        return (this.pageNumber > book.pageNumber) ? 1 : -1;
    }

    @Override
    public String toString() {
        return "Book{" +
                "author='" + author + '\'' +
                ", name='" + name + '\'' +
                ", pageNumber=" + pageNumber +
                '}';
    }
}

compareTo() 你将不得不@Override 方法,并定义用什么标准来比较这些实体。最后一般会归结为简单的原始类型,比如比较pageNumber 属性。我们还添加了一个toString() 方法,以方便格式化。

现在,在一个自定义对象的集合中搜索最大或最小的元素,就像在集合上调用Collections.max()Collections.min() 那样简单。

List<Book> bookList = new ArrayList<>();
bookList.add(new Book("Nick Bostrom", "Superintelligence", 352));
bookList.add(new Book("Ray Kurzweil", "The Singularity is Near", 652));
bookList.add(new Book("Max Tegmark", "Our Mathematical Universe", 432));

Book min = Collections.min(bookList);
Book max = Collections.max(bookList);

System.out.println(String.format("Minimum element is %s", min));
System.out.println(String.format("Maximum element is %s", max));

鉴于我们的比较标准,其结果是。

Minimum element is Book{author='Nick Bostrom', name='Superintelligence', pageNumber=352}
Maximum element is Book{author='Ray Kurzweil', name='The Singularity is Near', pageNumber=652}

自定义比较器

你_可以_避免使Book 比较,如果你不能通过在Collections 调用中提供一个新的Comparator 。虽然,这个解决方案很啰嗦,最好避免/用后面的技术来代替。

然而,这是一个完全有效的方法来比较实体并找到最大/最小值。

Book min = Collections.min(bookList, new Comparator<Book>() {
    @Override
    public int compare(Book o1, Book o2) {
        return (o1.getPageNumber() > o2.getPageNumber()) ? 1 : -1;
    }
});

System.out.println(String.format("Minimum by page count is %s", min));

或者,这可以通过一个Lambda表达式来缩短。

Book min = Collections.min(bookList, 
    (o1, o2) -> (o1.getPageNumber() > o2.getPageNumber()) ? 1 : -1);

用Stream.max()和Stream.min()获取最大或最小m元素

随着Java 8的出现,我们被引入了一个奇妙的_Stream API_,它允许我们执行各种处理管道。一个Stream ,可以通过max()min() 方法找到一个最大或最小的元素,利用一个新的Comparator 来完成这个工作,或者使用一个已经存在的,比如我们已经内置到我们的Book 类的比较器。

这允许你拥有非Comparable 类,并快速使用一个新的Comparator 来设置使用_任何领域_的标准。这种灵活性是使Stream在处理数据方面如此神奇的原因。

使用原始类型的Stream.max()和Stream.min()

让我们从一个简单的原始类型的集合开始。为了获得集合的最大或最小元素,我们stream() ,并调用min()max() 方法。

List<Integer> list = List.of(1, 5, 4, 3, 7, 6, 9, 4);
Integer maxInt = list.stream().mapToInt(i -> i).max().getAsInt();
Integer minInt = list.stream().mapToInt(i -> i).min().getAsInt();

System.out.println(String.format("Minimum element is %s", minInt));
System.out.println(String.format("Maximum element is %s", maxInt));

与其映射i -> i ,我们还可以直接使用。

Integer maxInt = list.stream().mapToInt(Integer::intValue).max().getAsInt();

max()min() 方法返回一个Optional - 或一个派生类,如OptionalInt,OptionalDouble, 等等。为了提取整数值--我们在调用链的末端使用getAsInt()

运行这段代码的结果是。

Minimum element is 1
Maximum element is 9

你也可以用一个Comparator ,例如comparing()comparingInt() (这两个方法都会产生相同的输出)来代替mapToInt()

Integer maxInt = list.stream().max(Comparator.comparing(Integer::intValue)).get();
Integer minInt = list.stream().min(Comparator.comparingInt(Integer::intValue)).get();

同样,这些方法返回一个Optional ,所以我们在最后get() 结果。comparing() 也可以灵活地比较非整数值,但由于我们在这里只限于整数,所以并没有什么区别。

使用自定义对象的Stream.max()和Stream.min()

在自定义对象中使用自定义比较器是Stream最闪亮的地方,也是它们提供最大灵活性的地方。当使用Collections 框架时,我们被限制在通过compareTo() 方法来比较元素,从Comparable 接口重载,如果你不想定义一个庞大的new Comparator

有了Streams--我们可以用任何字段即时定义一个新的Comparator ,甚至不需要类来实现Comparable 接口。让我们用自定义对象找到一个集合的最大和最小元素,使用一个自定义比较器和流。

List<Book> bookList = new ArrayList<>();
bookList.add(new Book("Nick Bostrom", "Superintelligence", 352));
bookList.add(new Book("Ray Kurzweil", "The Singularity is Near", 652));
bookList.add(new Book("Max Tegmark", "Our Mathematical Universe", 432));

// Using native compareTo() Method
Book min = bookList.stream().min(Book::compareTo).get();
Book max = bookList.stream().max(Book::compareTo).get();

// Using custom new Comparator
Book minByAuthor = bookList.stream().min(Comparator.comparing(Book::getAuthor)).get();
Book maxByAuthor = bookList.stream().max(Comparator.comparing(Book::getAuthor)).get();

System.out.println(String.format("Minimum by page count is %s", min));
System.out.println(String.format("Maximum by page count is %s", max));

System.out.println(String.format("Minimum by author is %s", minByAuthor));
System.out.println(String.format("Maximum by author is %s", maxByAuthor));

这一次,我们可以使用任何字段并将其提供给Comparator.comparing() 方法。你也可以使用其他方法,比如这里的comparingInt() ,但是由于我们要比较Strings的lexicographical value,所以我们将坚持使用通用的comparing() 方法。

运行这段代码的结果是。

Minimum by page count is Book{author='Nick Bostrom', name='Superintelligence', pageNumber=352}
Maximum by page count is Book{author='Ray Kurzweil', name='The Singularity is Near', pageNumber=652}

Minimum by author is Book{author='Max Tegmark', name='Our Mathematical Universe', pageNumber=432}
Maximum by author is Book{author='Ray Kurzweil', name='The Singularity is Near', pageNumber=652}

使用_for_循环

最后,良好的老for 循环可以用来搜索最大或最小的元素。

List<Book> bookList = new ArrayList<>();
bookList.add(new Book("Nick Bostrom", "Superintelligence", 352));
bookList.add(new Book("Ray Kurzweil", "The Singularity is Near", 652));
bookList.add(new Book("Max Tegmark", "Our Mathematical Universe", 432));

List<Integer> intList = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9);

// Instantiate as the first book initially
Book maxBook = bookList.get(0);
Integer maxInt = 0;

for (Book book : bookList) {
    // book.getPageNumber() < minBook.getPageNumber()
    if (book.getPageNumber() > maxBook.getPageNumber()) {
        maxBook = book;
    }
}

for (Integer integer : intList) {
    // integer < minInt
    if (integer > maxInt) {
        maxInt = integer;
    }
}

System.out.println(String.format("Maximum by page count is %s", maxBook));
System.out.println(String.format("Maximum int is %s", maxInt));

这个结果与我们到目前为止看到的结果相同。

Maximum by page count is Book{author='Ray Kurzweil', name='The Singularity is Near', pageNumber=652}
Maximum int is 9

结论

在本指南中,我们已经了解了如何找到一个Java集合的最大和最小元素。我们看了原始类型和自定义对象,默认和自定义比较器,以及最佳实践,包括一个手动for 循环。