java8: Stream 及相关

本文介绍 Java 8 新特性,包括默认方法,可让接口提供默认实现避免影响实现类;Supplier 和 Consumer 是一对逆运算接口;方法引用通过 :: 指向方法;Optional 类有常用方法;重点讲解 Stream 流,涵盖定义、创建、转换和汇聚操作,能简洁快速处理集合。

一、默认方法

​ 默认方法:接口提供默认的实现方法,可以不需要实现类去实现。

  • 为什么要有这个特性?

    在给接口添加新方法时,会影响所有实现该接口的实现类,如果提供了默认方法,如无必要,就不需要在实现类中去实现该方法。

  • 语法

public interface Vehicle {
   // 默认接口方法
   default void print() {
      System.out.println("一辆车");
   }
}
  • 子类调用默认方法

    接口名.super.默认方法名()

public interface Vehicle {
   default void print(){
      System.out.println("我是一辆车!");
   }
}

public interface FourWheeler {
   default void print(){
      System.out.println("我是一辆四轮车!");
   }
}

// 子类可以使用接口中定义的默认方法
public class Car implements Vehicle, FourWheeler {
   public void print(){
      Vehicle.super.print();
   }
}
  • 静态默认方法
public interface Vehicle {
    // 静态默认方法, 通过接口名直接调用
   static void warning(){
      System.out.println("按喇叭!");
   }
}

二、Supplier 和 Consumer

​ 两个函数是接口,可以看成是一对逆运算。一个作为生产工厂生产对象(Supplier),另一个作为消费者去消费对象(Consumer)。

  • Supplier接口

    接口就一个抽象方法get方法,不用传入任何参数,直接返回一个泛型T的实例。

@FunctionalInterface
public interface Supplier<T> {
    T get();
}
  • Consumer接口
  1. accept() 抽象方法,接收一个参数,没有返回值。
  2. andThen() 在执行完调用者方法后再执行传入参数的方法。
@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}
  • 实例
public static void main(String[] args) {
    Consumer<Integer> consumer = (x) -> {
        int num = x * 2;
        System.out.println(num);
    };
    Consumer<Integer> consumer1 = (x) -> {
        int num = x * 3;
        System.out.println(num);
    };
    consumer.andThen(consumer1).accept(10);
}
// Result
20
30

三、方法引用

​ 通过方法的名称来指向一个方法,方法引用使用一对冒号 ::

  • 示例
class Car {
    public static Car create(final Supplier<Car> supplier) {
        return supplier.get();
    }
 
    public static void collide(final Car car) {
        System.out.println("Collided " + car.toString());
    }
 
    public void follow(final Car another) {
        System.out.println("Following the " + another.toString());
    }
 
    public void repair() {
        System.out.println("Repaired " + this.toString());
    }
}
  • 构造方法引用

    语法:Class::new 或者 Class< T >::new

final Car car = Car.create( Car::new );
final List<Car> cars = Arrays.asList( car );
  • 静态方法引用

    语法:Class::static_method

cars.forEach( Car::collide );
  • 特定类的任意对象的方法引用

    语法:Class::method

cars.forEach( Car::repair );
  • 特定对象的方法引用

    语法: instance::method

final Car police = Car.create( Car::new );
cars.forEach( police::follow );

四、 Optional 类

1. 一个可以为null的容器对象,如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
2. Optional 类的引入很好的解决空指针异常。
  • 常用方法
方法描述
T get()如果包含值返回值,否则抛出:NoSuchElementException
void ifPresent(Consumer<? super T> consumer)如果值存在则使用该值调用 consumer , 否则不做任何事情
boolean isPresent()如果值存在则方法会返回true,否则返回 false。
static<T> Optional<T> of(T value)返回一个指定非null值的Optional
T orElse(T other)如果存在该值,返回值, 否则返回 other

五、Stream

1、定义

A sequence of elements supporting sequential and parallel aggregate operations.

​ 可以把Stream当成一个高级版本的Iterator。原始的 Iterator,用户只能一个一个的遍历元素并对其执行某些操作;高级的 Stream,用户只要给出需要对其包含的元素执行什么操作,具体这些操作如何应用到每个元素上,就给Stream就好!

  • 语法
// 创建          转换                          聚合
list.stream()  .filter(num -> num != null)   .count();
2、创建Stream
  1. 静态工厂创建
  • of() 方法
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4);  // 可变参数
Stream<String> stringStream = Stream.of("Hello");       // 单一参数
  • generator() 方法
// 原型
public static<T> Stream<T> generate(Supplier<T> s)// 三种使用方式
// 1. 匿名函数
Stream.generate(new Supplier<Double>() {
    @Override
    public Double get() {
        return Math.random();
    }
});
// 2. lambda表达式
Stream.generate(() -> Math.random());
// 3. 方法引用
Stream.generate(Math::random);
  • iterate() 方法
// 原型 -- 元素的生成是重复对给定的种子值(seed)调用用户指定函数来生成的。
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)// 示例  -- 获取无限长的正数集,并打印前5个(注意 limit限制长度)
Stream.iterate(1, item -> item + 1).limit(5).forEach(System.out::println);
  1. Collection子类获取Stream

    Collection的子类可以通过顶层接口 Collection 接口中 实现的默认方法 stream() 来获取。

// Collection 默认方法stream
default Stream<E> stream() {
	return StreamSupport.stream(spliterator(), false);
}
3、转换Stream

​ 把一个Stream通过某些行为转换成一个新的Stream,在 Stream 接口中定义了许多的转换方法,常见的如下。

  • distinct()

    流中元素去重
    在这里插入图片描述

  • filter()

    过滤符合条件的元素
    在这里插入图片描述

  • map()

    元素映射转换,类似的有 mapToInt,mapToLong 和 mapToDouble
    在这里插入图片描述

  • flatMap()

    把子Stream中的元素压缩到父集合中
    在这里插入图片描述

  • peek()

    生成一个包含原Stream的所有元素的新Stream,同时会提供一个消费函数(Consumer实例),新Stream每个元素被消费的时候都会执行给定的消费函数
    在这里插入图片描述

  • limit()

    截断操作,获取其前N个元素
    在这里插入图片描述

  • skip()

丢弃前N个元素
在这里插入图片描述

  • sorted()

    对流进行排序

Random random = new Random();
random.ints().limit(10).sorted().forEach(System.out::println);
  • 示例
List<Integer> nums = Arrays.asList(1,1,null,2,3,4,null,5,6,7,8,9,10);
System.out.println("sum is:" +  nums.stream()
                   .filter(num -> num != null)
                   .distinct()
                   .mapToInt(num -> num * 2)
                   .peek(System.out::print)
                   .skip(2)
                   .limit(4)
                   .sum()
);
// Result
24681012sum is:36
  • 效率问题

    转换操作都是lazy的,所有的转换都是一次循环完成

4、汇聚Stream
  1. 可变汇聚

    把输入的元素们累积到一个可变的容器中,如Collection或者StringBuilder

  • collect() 方法
// 原型
<R> R collect(Supplier<R> supplier,       // 1. 工厂函数,用来生成一个新的容器
    BiConsumer<R, ? super T> accumulator, // 2. 函数,把元素添加到结果容器中
    BiConsumer<R, R> combiner);  // 3. 函数,把中间状态的多个结果容器合并成为一个(并发的时候会用到)

// 示例
List<Integer> nums = Arrays.asList(1,1,null,2,3,4,null,5,6,7,8,9,10);
List<Integer> numsWithoutNull = nums.stream()
    .filter(num -> num != null)
    .collect(() -> new ArrayList<Integer>(),  // 1
             (list, item) -> list.add(item),  // 2
             (list1, list2) -> list1.addAll(list2));  // 3
  1. 生成一个新的ArrayList实例
  2. 接受两个参数,第一个是前面生成的ArrayList对象,二个是stream中包含的元素,函数体就是把stream中的元素加入ArrayList对象中。第二个函数被反复调用直到原stream的元素被消费完毕;
  3. 接受两个参数,这两个都是ArrayList类型的,函数体就是把第二个ArrayList全部加入到第一个中(并发的时候会用到)。
 **简化**

 使用 Collector 工具类对上述汇聚函数进行简化。
// 原型
<R, A> R collect(Collector<? super T, A, R> collector);
// 上个例子的简化版
List<Integer> numsWithoutNull = nums.stream().
    filter(num -> num != null)
    .collect(Collectors.toList());
  1. 其他汇聚
  • reduce() 方法

    接受的函数有两个参数,第一个参数是上次函数执行的返回值(也称为中间结果),第二个参数是stream中的元素,这个函数把这两个值相加,得到的和会被赋值给下次执行这个函数的第一个参数。

// 原型
Optional<T> reduce(BinaryOperator<T> accumulator);
T reduce(T identity, BinaryOperator<T> accumulator);

// 示例1  -- 没有初值,第一次执行时第一个参数是Stream的第一个元素,第二个参数是Stream的第二个元素
List<Integer> ints = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
System.out.println("ints sum is:" + ints.stream()
                   .reduce((sum, item) ->sum + item)
                   .get());  // Optional类型,get()获取值

// 示例2  -- 有初值,第一次执行时第一个参数的值,第二个参数是Stream的第一个元素
List<Integer> ints = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
System.out.println("ints sum is:" + ints.stream()
                   .reduce(0, (sum, item) -> sum + item));
  • count() 方法

    获取Stream中元素的个数

List<Integer> ints = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
System.out.println("ints sum is:" + ints.stream().count());
  • 统计
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
 
IntSummaryStatistics stats = numbers.stream()
    .mapToInt((x) -> x).summaryStatistics();
 
System.out.println("列表中最大的数 : " + stats.getMax());
System.out.println("列表中最小的数 : " + stats.getMin());
System.out.println("所有数之和 : " + stats.getSum());
System.out.println("平均数 : " + stats.getAverage());
  1. JAVA8之函数式编程Supplier接口和Consumer接口
  2. 菜鸟教程-Java 8 新特性
  3. 并发编程网 – Stream语法详解
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值