• 开发使用Stream类数据方法和计算方法的代码。
  • 使用 java.util.Comparator和 java.lang.Comparable接口。
  • 使用流 API 对集合进行排序。

Comparator和Comparable

  • java.util.Comparator
    util包下,N个方法,但因为是函数式接口,所以其他方法均为default或static方法
    image.png
  • java.lang.Comparable
    因为在lang包下,不需要导包,只有一个abstract方法
    image.png

区别

public interface Comparator<T> {
    int compare(T obj1, T obj2);

    // Other default and static methods ...
}
public interface Comparable<T> {
    int compareTo(T obj);
}
  • Comparator
    2个入参,创建一个obj来比较另一种类型的两个obj,以对它们进行排序; 这就是为什么需要两个参数
  • Comparable
    1个入参,一个obj与同类型的另一个obj进行比较,以对它们进行排序

java.lang.Comparable

int compareTo(T obj)
  • 返回0,则obj equals入参
  • 大于0,则obj>入参
  • 小于0,则obj<入参
    Java 的许多类(如 BigDecimal、 BigInteger、 Integer、 String 等包装器)以自然顺序(如1、2、3、4或 a、 b、 c、 a、 b、 c)实现这个接口。
    这个方法可以用来测试一个对象是否与另一个对象相等,所以建议实现与 equals (Object)方法一致(如果 compareTo 方法返回0,equals 方法必须返回 true)

实现了这个接口,就可以按 Collections.sort ()或 Arrays.sort ()进行排序。它还可以用作排序映射(如 TreeMap)或排序集(如 TreeSet)中的键。

class Computer implements Comparable<Computer> {
    private String brand;
    private int id;
    public Computer(String brand, int id) {
        this.brand = brand;
        this.id = id;
    }
    // Let's compare first by brand and then by id
    @Override
    public int compareTo(Computer other) {
        // Reusing the implementation of String
        int result = this.brand.compareTo(other.brand);
        // If the objects are equal, compare by id
        if (result == 0) {
            // Let's do the comparison "manually"
            // instead of using Integer.valueOf(this.id).compareTo(other.id)
            // or Integer.compare(this.id, other.id)
            if (this.id > other.id) {
                result = 1;
            } else if (this.id < other.id) {
                result = -1;
            }
            // else result = 0;
        }
        return result;
    }
    // equals and compareTo must be consistent
    // to avoid errors in some cases
    @Override
    public boolean equals(Object other) {
        if (this == other) {
            return true;
        }
        if (!(other instanceof Computer)) {
            return false;
        }
        return this.brand.equals(((Computer) other).brand)
                && this.id == ((Computer) other).id;
    }

    public static void main(String[] args) {
        Computer c1 = new Computer("Lenovo", 1);
        Computer c2 = new Computer("Apple", 2);
        Computer c3 = new Computer("Dell", 3);
        Computer c4 = new Computer("Lenovo", 2);
        // Some comparisons
        System.out.println(c1.compareTo(c1)); // c1 == c1
        System.out.println(c1.compareTo(c2)); // c1 > c2
        System.out.println(c2.compareTo(c1)); // c2 < c1
        System.out.println(c1.compareTo(c4)); // c1 < c2
        System.out.println(c1.equals(c4)); // c1 != c2
        // Creating a list and sorting it
        List<Computer> list = Arrays.asList(c1, c2, c3, c4);
        Collections.sort(list);
        list.forEach(
                c -> System.out.format("%s-%d\n", c.brand, c.id)
        );
    }
}

image.png
Comparable也是函数式接口但是JDK没有加@FunctionalInterface注解,是由于 Comparable 被认为是由被比较的对象实现的,所以你几乎不会把它用作 lambda 表达式

java.util.Comparator

int compare(T o1, T o2);
  • 返回0,则入参1 equals 入参1
  • 大于0,则入参1>入参1
  • 小于0,则入参1<入参1

优点: Comparator 以不同的方式对同一对象进行排序

  • 定义排序规则
    1.内部类
Comparator<Computer> sortById = (c1, c2) -> {
    int result = Integer.compare(c1.id, c2.id);
    return result == 0
       ? c1.brand.compareTo(c2.brand) : result;
}

2.lambda表达式

Comparator<Computer> sortById1 = (c11, c21) -> {
            int result = Integer.compare(c11.id, c21.id);
            return result == 0
                    ? c1.brand.compareTo(c21.brand) : result;
        };
  • 使用
    image.png
    Collections类定义了静态static方法
List<Computer> list1 = Arrays.asList(c1, c2, c3, c4);
        Collections.sort(list1, sortById1);
        list.forEach(
                c -> System.out.format("%d-%s\n",c.id,c.brand)
        );

Comparator的其他方法

Java 8中,随着接口中默认default和静态static方法的引入,Comparator有一些有用的方法来简化我们的代码

comparing和thenComparing方法

image.png

Comparator<T>
   Comparator.comparing(Function<? super T, ? extends U>)
Comparator<T>
   Comparator.comparingInt(ToIntFunction<? super T>)
Comparator<T>
   Comparator.comparingLong(ToLongFunction<? super T>)
Comparator<T>
   Comparator.comparingDouble(ToDoubleFunction<? super T>)

image.png
接口的静态static方法
接受一个函数(一个 lambda 表达式) ,该函数返回对象的一个属性的值,该属性将使用 comparedTo 返回的值创建一个 Comparator

  • 通过这种方式,可以简化代码
Comparator<Computer> sortByIdThenByBrand =
        Comparator.comparing((Computer c) -> c.id)
            .thenComparing(c -> c.brand);

reverse方法

Comparator,它将逆转原始 Comparator 的顺序
image.png

对Stream流数据进行排序

  • 按原始顺序排序
Stream<T> sorted()

返回一个按自然顺序排序的stream流
其中stream流中的元素必须要实现java.lang.Comparable接口,否则会抛出ClassCastException异常

  • 按自定义顺序排序
    采用java.util.Comparator(这个版本不适用于像 IntStream 这样的基本类型流)
Stream<T> sorted(Comparator<? super T> comparator)
 List<String> strings =
                Arrays.asList("Stream", "Operations", "on", "Collections");
        strings.stream()
                //如果第一个字符串的长度小于第二个字符串的长度,那么第一段代码将返回一个正值
                .sorted((s1, s2) -> s2.length() - s1.length())
                .forEach(System.out::println);
        strings.stream()
                .sorted(Comparator.comparing(
                        //自然顺序(升序),然后反转这个顺序
                        String::length).reversed())
                .forEach(System.out::println);

数据和计算方法

Stream接口提供以下数据和计算方法:

比较大小 max min

//返回流中的元素数,如果流是空的,返回零
long count()
//返回包装在 Optional 中的流中的最大值,如果流为空,则返回空值
Optional<T> max(Comparator<? super T> comparator)
//返回包装在 Optional 中的流中的最小值,如果流为空,则返回空值
Optional<T> min(Comparator<? super T> comparator)

对于基本类型的Steam流有类似方法

  • IntStream

  • LongStream

  • DoubleStream

  • 比较值大小注意点
    当为基本类型时,很容易知道哪个是最小值或最大值。但是为包装类型时,Java需要知道如何比较它们,以便知道哪个是最大值和最小值。这就是为什么Stream接口需要一个用于max()和min()的Comparator

List<String> strings =
Arrays.asList("Stream","Operations","on","Collections");
strings.stream()
    .min( Comparator.comparing(
                 (String s) -> s.length())
    ).ifPresent(System.out::println); // print on

sum

int sum();

返回流中元素的和,如果流是空的,则返回零

System.out.println(
    IntStream.of(28,4,91,30).sum()
); // 153

average

OptionalDouble average();

回包装在 OptionalDouble 中的流中元素的平均值,如果流为空,则返回空元素的平均值

System.out.println(
    IntStream.of(28,4,91,30).average()
); // 38.25

总结

  • 当返回0时,表示对象(或第一个参数)等于(第二个)参数。
  • 当返回大于零的数字时,表示对象(或第一个参数)大于(第二个)参数。
  • 当返回小于零的数字时,表示对象(或第一个参数)小于(第二个)参数。
  • Stream接口的sorted()方法返回一个流,其中的元素按其自然顺序排序。也可以将比较器作为参数传递。
  • count()返回流中的元素数,如果流为空,则返回零。
  • min()返回流中的最小值,如果流为空,则返回可选值或空值。
  • max()返回流中的最大值,如果流为空,则返回可选值或空值。
  • sum()返回流中元素的和,如果流为空,则返回零。
  • average()返回流中元素的平均值,如果流为空,则返回一个OptionalDouble或空元素。

这个家伙很懒,啥也没有留下😋