java8 Stream流式编程

stream 的处理效率会高于 iterator,特别是使用了并行流,在cpu恰好将线程分配到多个核心的条件下(当然parallel stream 底层使用的是 JVM 的 ForkJoinPool,这东西分配线程本身就很玄学),可以达到一个很高的运行效率,然而实际普通业务一般不会有需要迭代高于10000次的计算;
111
在这里插入图片描述
在这里插入图片描述

流创建

  1. Stream.of()
    通过 Stream.of() 可以很容易地将一组元素转化为流
Stream.of(new Bubble(1), new Bubble(2), new Bubble(3)).forEach(System.out::println);
Stream.of("a", "b", "c", "d", "e", "f").forEach(System.out::print);
Stream.of(3.14159, 2.718, 1.618).forEach(System.out::println);
  1. stream()
    每个集合也可以通过调用 stream() 方法来产生一个流
List<Bubble> list = Arrays.asList(new Bubble(1), new Bubble(2), new Bubble(3));
list.stream().forEach(System.out::print);
Set<String> set = new HashSet<>(Arrays.asList("a", "b", "c", "d", "e", "f"));
set.stream().forEach(System.out::print);
  1. Stream.generate()
    使用 Stream.generate() 搭配 Supplier 生成 T 类型的流
Stream.generate(Math::random).limit(10).forEach(System.out::print);
  1. Stream.iterate()
    Stream.iterate() 产生的流的第一个元素是种子,然后把种子传递给方法,方法的运行结果被添加到流,并作为下次调用 iterate() 的第一个参数
Stream.iterate(0, n -> n + 1).limit(10).forEach(System.out::print)

使用 Stream.generate() 和 Stream.iterate() 生成的无限流一定要用 limit() 截断
5. Stream.builder()
使用建造者模式创建一个 builder 对象,然后将创建流所需的多个信息传递给它,最后 builder 对象执行创建流的操作

Stream.Builder<String> builder = Stream.builder();
builder.add("a");
builder.add("b");
...
builder.build();	// 创建流
// builder.add("c")	// 调用 build() 方法后继续添加元素会产生异常
  1. Arrays.stream()
    Arrays 类中有一个名为 stream() 的静态方法用于把数组转换成流
Arrays.stream(new double[] {3.14159, 2.718, 1.618}).forEach(System.out::print);
Arrays.stream(new int[] {1, 3, 5}).forEach(System.out::print);
Arrays.stream(new long[] {11, 22, 44, 66}).forEach(System.out::print);
// 选择一个子域
Arrays.stream(new int[] {1, 3, 5, 7, 15, 28, 37}, 3, 6).forEach(System.out::print);

最后一次 stream() 的调用有两个额外的参数,第一个参数告诉 stream() 从数组的哪个位置开始选择元素,第二个参数告知在哪里停止
7. IntStream.range()
IntStream 类提供 range() 方法用于生成整型序列的流,编写循环时,这个方法会更加便利

IntStream.range(10, 20).sum();	// 求得 10 - 20 的序列和
IntStream.range(10, 20).forEach(System.out::print);	// 循环输出 10 - 20
  1. 随机数流
    Random 类被一组生成流的方式增强了,可以生成一组随机数流
Random rand = new Random(47);
// 产生一个随机流
rand.ints().boxed();
// 控制上限和下限
rand.ints(10, 20).boxed();
// 控制流的大小
rand.ints(2).boxed();
// 控制流的大小和界限
rand.ints(3, 3, 9).boxed();

Random 类除了能生成基本类型 int,long,double 的流,使用 boxed() 操作会自动把基本类型包装为对应的装箱类型
9. 正则表达式
Java8 在 java.util.regex.Pattern 中新增了一个方法 splitAsStream(),这个方法可以根据传入的公式将字符序列转化为流
Pattern.compile("[.,?]+").splitAsStream(“a,b,c,d,e”).forEach(System.out::print);

在这里插入图片描述

public class StreamFeaturesTest {

    /**
     * 流的简单例子
     */
    @Test
    public void test1() {
        List<Integer> list =  Stream.of(1, 2, 5, 9, 7, 3).filter(val-> val> 2).sorted().collect(Collectors.toList());
        for (Integer item : list) {
            System.out.println(item);
        }
    }
    /**
     * 流不会改变数据源
     */
    @Test
    public void test2() {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(1);
        Assert.assertEquals(3, list.stream().distinct().count());
        Assert.assertEquals(4, list.size());
    }

    /**
     * 流不可以重复使用
     */
    @Test(expected = IllegalStateException.class)
    public void test3() {
        Stream<Integer> integerStream = Stream.of(1, 2, 3);
        Stream<Integer> newStream = integerStream.filter(val -> val > 2);
        integerStream.skip(1);
    }
}

在这里插入图片描述
在这里插入图片描述

中间操作

  1. 跟踪和调试

peek() 操作的目的是帮助调试,它提供了一种对流中所有元素操作的方法,同时提供一个消费函数,对流中元素进行操作,并返回一个新流。一般不建议这样做,更多的用途应该是无修改地查看流中的元素

Stream.of("a b c d e".split(" ")).map(w -> w + " ").peek(System.out::print);
  1. 流元素排序

sorted() 可以帮助我们实现对流元素的排序,如果不使用默认的自然排序,则需要传入一个比较器,也可以把 Lambda 函数作为参数传递给 sorted()

Stream.of("a b c d e".split(" ")).sorted(Comparator.reverseOrder())
    .map(w -> w + " ").peek(System.out::print);
  1. 移除元素

distinct() 可用于消除流中的重复元素

new Random(47).ints(5, 20).distinct().limit(7).forEach(System.out::println);

filter(Predicate) 将元素传递给过滤函数,若结果为 true,则保留元素

// 检测质数
Stream.iterate(2, n -> n + 1).filter(i -> i % 2 ==0)
    .limit(10).forEach(System.out::print)
  1. 应用函数到元素

map(Function) 将函数操作应用到输入流的元素,并将返回值传递到输出流

Arrays.stream(new String[] {"12", "23", "34"}).map(s -> "[" + s + "]")
    .forEach(System.out::print)

另外还有 mapToInt(ToIntFunction)、mapToLong(ToLongFunction)、mapToDouble(ToDoubleFunction),操作和 map(Function) 相似,只是结果流为各自对应的基本类型

如果在将函数应用到元素的过程中抛出了异常,此时会把原始元素放到输出流

  1. 组合流

使用 flatMap() 将产生流的函数应用在每个元素上,然后将产生每个流都扁平化为元素

Stream.of(1, 2, 3).flatMap(i -> Stream.of("hello" + i)).forEach(System.out::println);

另外还有 flatMapToInt(Function)、flatMapToLong(Function)、flatMapToDouble(Function),操作和 flatMap() 相似,只是结果元素为各自对应的基本类型

Optional 类

如果在一个空流中尝试获取元素,结果肯定是得到一个异常。我们希望可以得到友好的提示,而不是糊你一脸 NullPointException。Optional 的出现就是为了解决臭名昭著的空指针异常

  1. findFirst() :返回一个包含第一个元素的 Optional 对象,如果流为空则返回 Optional.empty
  2. findAny():返回包含任意元素的 Optional 对象,如果流为空则返回 Optional.empty
  3. max() 和 min():返回一个包含最大值或者最小值的 Optional 对象,如果流为空则返回 Optional.empty
  4. reduce(Function):将函数的返回值包装在 Optional 中

1. 便利函数

Optional 类本质上是一个容器对象,所谓容器是指:它可以保存类型 T 的值,也可以保存一个 null。此外,Optional 提供了许多有用的方法,可以帮助我们不用显示地进行空值检测:

  1. ifPresent():是否有值存在,存在放回 true,否则返回 false
  2. ifPresent(Consumer):当值存在时调用 Consumer,否则什么也不做
  3. orElse(otherObject):如果值存在则直接返回,否则生成 otherObject
  4. orElseGet(Supplier):如果值存在则直接返回,否则使用 Supplier 函数生成一个可替代对象
  5. orElseThrow(Supplier):如果值存在则直接返回,否则使用 Supplier 函数生成一个异常
class OptionalBasics {
    
    static void test(Optional<String> optString) {
        if(optString.isPresent())
            System.out.println(optString.get()); 
        else
            System.out.println("Nothing inside!");
    }
    
    public static void main(String[] args) {
        test(Stream.of("Epithets").findFirst());
        test(Stream.<String>empty().findFirst());	// 生成一个空流
    }
}

2.创建 Optional

当我们需要在自己的代码中加入 Optional 时,可以使用下面三个静态方法:

  1. empty():生成一个空 Optional
  2. of(value):将一个非空值包装到 Optional 里
  3. ofNullable(value):针对一个可能为空的值,为空时自动生成 Optional.empty,否则将值包装在 Optional 中

3. Optional 对象操作

当我们的流管道生成 Optional 对象,下面三个方法可以使得 Optional 能做更多后续操作:

  1. filter(Predicate):对 Optional 中的内容应用 Predicate 并将结果返回。如果 Optional 不满足 Predicate,将 Optional 转化为空 Optional 。如果 Optional 已经为空,则直接返回空 Optional
  2. map(Function):如果 Optional 不为空,应用 Function 于 Optional 中的内容,并返回结果,否则直接返回 Optional.empty
  3. flatMap(Function):一般应用于已生成 Optional 的映射函数,所以 flatMap() 不会像 map() 那样将结果封装在 Optional 中

终端操作

终端操作将获取流的最终结果,至此我们无法再继续往后传递流。可以说,终端操作总是我们在使用流时所做的最后一件事

  1. 数组

当我们需要得到数组类型的数据以便于后续操作时,可以使用下述方法产生数组:
toArray():将流转换成适当类型的数组
toArray(generetor):生成自定义类型的数组

  1. 循环

常见的如 forEach(Consumer),另外还有 forEachOrdered(Consumer),保证按照原始流的顺序操作。第二种形式仅在引入并行流时才有意义。所谓并行流是将流分割为多个,并在不同的处理器上分别执行。由于多处理器并行操作的原因,输出的结果可能会不一样,因此需要用到 forEachOrdered(Consumer)

  1. 集合

在这里我们只是简单介绍一下常见的 Collectors 示例,实际上它还有一些非常复杂的实现。大多数情况下,java.util.stream.Collectors 中预设的 Collector 就能满足我们的需求
collect(Collector):使用 Collector 收集流元素到结果集合中
collect(Supplier, BiConsumer, BiConsumer):第一个参数创建一个新的结果集合,第二个参数将下一个元素收集到结果集合中,第三个参数用于将两个结果集合合并起来

  1. 组合

组合意味着将流中所有元素以某种方式组合为一个元素
reduce(BinaryOperator):使用 BinaryOperator 来组合所有流中的元素。因为流可能为空,其返回值为 Optional
reduce(identity, BinaryOperator):功能同上,但是使用 identity 作为其组合的初始值。因此如果流为空,identity 就是结果

Stream.generate(Math::random).limit(10)
	.reduce((fr0, fr1) -> fr0.size < 50 ? fr0 : fr1).ifPresent(System.out::println);

返回的结果是 Optional 类型,Lambda 表达式中的第一个参数 fr0 是 reduce 中上一次调用的结果,而第二个参数 fr1 是从流传递过来的值

  1. 匹配

allMatch(Predicate):如果流的每个元素提供给 Predicate 都返回 true ,结果返回为 true。在第一个 false 时,则停止执行计算
anyMatch(Predicate):如果流的任意一个元素提供给 Predicate 返回 true ,结果返回为 true。在第一个 true 是停止执行计算
noneMatch(Predicate):如果流的每个元素提供给 Predicate 都返回 false 时,结果返回为 true。在第一个 true 时停止执行计算

  1. 查找

findFirst():返回第一个流元素的 Optional,如果流为空返回 Optional.empty
findAny():返回含有任意流元素的 Optional,如果流为空返回 Optional.empty

  1. 信息

count():流中的元素个数
max(Comparator):根据所传入的 Comparator 所决定的最大元素
min(Comparator):根据所传入的 Comparator 所决定的最小元素

  1. 数字流信息
    average():求取流元素平均值
    max() 和 min():数值流操作无需 Comparator
    sum():对所有流元素进行求和

在这里插入图片描述

public class Apple {
    private int id;            // 编号
    private String color;      // 颜色
    private int weight;        // 重量
    private String birthplace; // 产地

    public Apple(int id, String color, int weight, String birthplace) {
        this.id = id;
        this.color = color;
        this.weight = weight;
        this.birthplace = birthplace;
    }

    // getter/setter 省略
}
public class StreamTest {

    private static final List<Apple> appleStore = Arrays.asList(
            new Apple(1, "red", 500, "湖南"),
            new Apple(2, "red", 100, "天津"),
            new Apple(3, "green", 300, "湖南"),
            new Apple(4, "green", 200, "天津"),
            new Apple(5, "green", 100, "湖南")
    );
    public static void main(String[] args) {
        appleStore.stream().filter(apple -> apple.getWeight() > 100)
                .peek(apple -> System.out.println("通过第1层筛选 " + apple))
                .filter(apple -> "green".equals(apple.getColor()))
                .peek(apple -> System.out.println("通过第2层筛选 " + apple))
                .filter(apple -> "湖南".equals(apple.getBirthplace()))
                .peek(apple -> System.out.println("通过第3层筛选 " + apple))
                .collect(Collectors.toList());
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值