stream流-学习笔记

stream流

概述:

Stream是一个来自数据源的元素队列,Stream并不会存储元素,而是按需计算,数据源流的来源可以是集合和数组,类似于Iterable的集合类。能够以一种声明的方式处理数据源(集合、数组等),它专注于对数据源进行各种高效的聚合操作(aggregate operation)和大批量数据操作 (bulk data operation)。

关键概念:
1.元素 Stream是一个来自数据源的元素队列,Stream本身并不存储元素。
2.数据源(即Stream的来源)包含集合、数组、I/O channel、generator(发生器)等。
3.聚合操作 类似SQL中的filter、map、find、match、sorted等操作
4.管道运算 Stream在Pipeline中运算后返回Stream对象本身,这样多个操作串联成一个Pipeline,并形成fluent风格的代码。这种方式可以优化操作,如延迟执行(laziness)和短路( short-circuiting)。
5.内部迭代 不同于java8以前对集合的遍历方式(外部迭代),Stream API采用访问者模式(Visitor)实现了内部迭代。
6.并行运算 Stream API支持串行(stream() )或并行(parallelStream() )的两种操作方式。

Stream操作有两个基础的特征:
1.Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。

2.内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。
两种实现流方式:
1.常用方式(返回一个顺序流)
List<String> list = Arrays.asList("test", "hello", "hello world", "fright");
Stream<String> stream = list.stream();
2.多线程(并行流),必需考虑线程安全问题
List<Employee> list = new ArrayList<>();
//普通流
Stream<Employee> stream = list.stream();
//并行流
Stream<Employee> parallelStream = list.parallelSteam();
两种函数式创建流的方式:
1.迭代
Stream<Integer> stream = Stream.iterate(0, (x) -> x + 2).limit(2);
stream.forEach(System.out::println);
2.生成(创建无限流)
Stream<Double> generate = Stream.generate(new Supplier<Double>(){//函数接口生产者接口
    @Override
    public Double get(){
        return java.lang.Math.random();
    }
});
//generate实现了生产者函数接口,所以可以使用lambda
Stream<Double> generateB = Stream.generate(() -> java.lang.Math.random());
Stream<Double> generateC = Stream.generate(java.lang.Math::random).limit(4);
generateC.forEach((System.out::println));
Api测试:
1.filter过滤: stream.filter(s -> s.isEmpty()).forEach(System.out::println)

​ 参数:filter(参数 -> 判断式(返回为Boolean))

@Test
public void testFilter(){
	List<String> strings = Arrays.asList("test", "hello", "Hello", "HelloWorld", "小学鸡加油");
    //过滤出hello的字符串并输出
	strings.stream().filter(s -> s.equals("hello")).forEach(System.out::println);
}
2.concat合并两个流

若两个输入的Stream都是排序的,则新Stream也是排序的;

若输入的Stream中任何一个是并行的,则新的Stream也是并行的;

若关闭新的Stream时,则两个输入的Stream都将执行关闭处理。

@Test
public void testConcat(){
    Stream<Integer> s1 = Stream.of(1, 2);
    Stream<Integer> s2 = Stream.of(2, 4, 8)
    Stream.concat(s1, s2).forEach(num -> System.out.print(num + " "));
}
3.映射map(实际操作流内的元素并可以返回)

该方法接收一个Function函数式接口作为参数,可以将当前流中的T类型转换为另一种R类型。

方法描述
map(Function f)接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
mapToDouble(ToDoubleFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。
mapToInt(ToIntFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。
mapToLong(ToLongFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。
flatMap(Function f)接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
@Test
public void testMap(){
    Stream<String> mapTarget = Stream.of("11", "22", "9");
    Stream<Integer> mapRes = mapTarget.map((s -> Integer.parseInt(s)));
    mapRes.forEach(s -> System.out.print(s + " "));
}
4.flatMap

flatmap()是将原Stream的元素取代为转换的Stream。

@Test
public void testFlatmap(){
    //该Stream会被后面那个处理过的Stream替换
    Stream.of(1, 2, 3).flatMap(num -> Stream.of(num * 10)).forEach(System.out::println);
    //输出结果:10 20 30
}
5.reduce计算流的元素(重点)

reduce方法有三个重载的方法。

1.使用lambda表达式实现计算操作
@Test
public void testFirst(){
    List<Integer> numList = Arrays.asList(1,2,3,4,5);
    int result = numList.stream().reduce((a,b) -> a + b ).get();
    System.out.println(result);
}

2.该方法首次执行时表达式第一次参数并不是stream的第一个元素,而是通过签名的第一个参数identity来指定。
用处:需要在不改变原集合时增加条件
@Test
public void testSecond(){
    List<Integer> numList = Arrays.asList(1,2,3,4,5);
    //计算过程: 2 + 1 + 2 ... + 5
    int result = numList.stream().reduce(2,(a,b) ->  a + b );
    System.out.println(result);
}

3.前两种实现有一个缺陷,它们的计算结果必须和stream中的元素类型相同,因此加入第三个签名
方法原型:<U> U reduce(U identity,
                	BiFunction<U, ? super T, U> accumulator,
                 	BinaryOperator<U> combiner);
用处:但返回的结果与原来集合的结果类型不同时,可以使用参数指定返回结果类型
@Test
public void testThrid(){
    List<Integer> numList = Arrays.asList(1, 2, 3, 4, 5, 6);
        List<String> result = numList.stream().reduce(new ArrayList<String>(), (a, b) -> {
            a.add("int转成string:" + Integer.toString(b));
            return a;
        }, (a, b) -> null);
        System.out.println(result);
}
4.参数解析:
使用了parallelStream reduce操作是并发进行的,为了避免竞争,每个reduce线程都会有独立的result combiner的作用在于合并每个线程的result得到最终结果。
BinaryOperator是供多线程使用的,如果不在Stream中声明使用多线程,就不会使用子任务,自然也不会调用到该方法。另外多线程下使用BinaryOperator的时候是需要考虑线程安全的问题。
6.peek消费Stream里面的元素(只对元素进行操作,对结果没有改变)

生成一个包含原Stream的所有元素的新Stream,同时会提供一个消费函数(Consumer的实例),新Stream每个元素被消费的时候都会优先执行给定的消费函数

用处:主要用于Debug的时候看到对应消费的元素
@Test
public void testPeekWork(){
    Stream.of("one", "two", "three","four")
                .peek(e -> System.out.println("Filtered value: " + e))
                .map(String::toUpperCase)
                .peek(e -> System.out.println("Mapped value: " + e))
                .collect(Collectors.toList());
}

结果: ONE TWO THREE FOUR
@Test
public void testPeekUnwork(){
     Stream.of("one", "two", "three","four")
                .peek(e -> e.toUpperCase()).collect(Collectors.toList());
}

结果: one two three four

但对对象进行的操作会进行保存(重点)
@Test
public void testPeekObject(){
    List<User> userList=Stream.of(new User("a"),new User("b"),new User("c")).peek(u->u.setName("kkk")).collect(Collectors.toList());
        userList.forEach(s -> System.out.println(s.getName()));
} 
结果: kkk kkk kkk
7.count统计个数

count是统计流中的元素个数

@Test
public void testCount(){
    Stream<String> stream = Stream.of("张三", "张无忌", "李四", "王五");
    //统计张姓的个数,,count函数使用后会关闭流对象(即后面不能再调用stream)
    long sum = stream.filter(s -> s.startsWith("张")).count();
    System.out.println(sum);
}
8.limit对流的元素进行截取

limit可以对流进行截取,支取前几个,参数是一个long类型,如果集合当前长度大于参数,则进行截取,否则不进行操作。

@Test
public void testLimit(){
    Stream<Integer> stream = Stream.of(1, 23, 43, 142);
    stream.limit(3).forEach(num -> System.out.println(num));
}
9.skip跳过流的前几个元素

希望跳过前几个元素,则可以使用skip方法获取一个截取之后的新流。

参数是一个long类型,如果流的当前长度大于n,则跳过前n个,否则,将会得到一个长度为0的空流。

@Test
public void testSkip(){
    Stream<Integer> stream = Stream.of(1, 23, 43, 142);
    stream.skip(2).forEach(num -> System.out.println(num));
}
10.sorted对流的元素进行排序

默认是从小到大排序,可以使用函数进行自定义排序

@Test
public void testSorted(){
    List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
	numbers.stream().sorted().forEach(System.out::println);
}
结果: 2 2 3 3 3 5 7
    
@Test
public void testSortedFunction(){
    List<Integer> list = Arrays.asList(2, 3, 1, 5, 9);
    //函数的方式
    list.stream().sorted((a, b) -> a - b).forEach(System.out::println);
}
结果: 1 2 3 5 9
11.distinct对流内元素进行去重
@Test
public void testDistinct(){
    List<Integer> numbers = Arrays.asList(3, 3, 7, 3, 5);
	numbers.stream().distinct().forEach(System.out::println);
}
结果: 3 7 5
12.结合使用
@Test
public void testDemo(){
    List<String> strings = Arrays.asList("", "lucas", "Hello", "HelloWorld", "武汉加油");
        Stream<Integer> stream = strings.stream()
                .filter(string -> string.length() <= 6) //过滤掉了"HelloWorld"
                .map(String::length)  //将Stream从原来元数据的String类型变成了int
                .sorted() //int从小到大
                .limit(2) //只选前两个
                .distinct(); //去重
       	stream.forEach( System.out::println);
}
Stream的终止操作

最终操作会消耗流,产生一个最终结果。也就是说,在最终操作之后,不能再次使用流,也不能在使用任何中间操作,否则将抛出异常。

方法描述
allMatch(Predicate p)检查是否匹配所有元素
anyMatch(Predicate p)检查是否至少匹配一个元素
noneMatch(Predicate p)检查是否没有匹配所有元素
findFirst()返回第一个元素
findAny()返回当前流中的任意元素
count()返回流中元素总数
max(Comparator c)返回流中最大值
min(Comparator c)返回流中最小值
forEach(Consumer c)内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部 迭代——它帮你把迭代做了)
Collect方法的使用

collect是一个归约操作,可以接受各种做法作为参数,将流中的元素累积成一个汇总结果。

方法返回类型作用实例
toListList把流中元素收集到ListList emps= list.stream().collect(Collectors.toList());
toSetSet把流中元素收集到SetSet emps= list.stream().collect(Collectors.toSet());
toCollectionCollection把流中元素收集到创建的集合Collectionemps=list.stream().collect(Collectors.toCollection(ArrayList::new));
countingLong计算流中元素的个数long count = list.stream().collect(Collectors.counting());
summingIntInteger对流中元素的整数属性求和inttotal=list.stream().collect(Collectors.summingInt(Employee::getSalary));
joiningString连接流中每个字符串String str= list.stream().map(Employee::getName).collect(Collectors.joining());
groupingByMap<K, List>根据某属性值对流分组,属 性为K,结果为VMap<Emp.Status, List> map= list.stream() .collect(Collectors.groupingBy(Employee::getStatus));
partitioningByMap<Boolean, List>根据true或false进行分区Map<Boolean,List>vd= list.stream().collect(Collectors.partitioningBy(Employee::getManage));
reducing归约产生的类型从一个作为累加器的初始值 开始,利用BinaryOperator与 流中元素逐个结合,从而归约成单个值int total=list.stream().collect(Collectors.reducing(0, Employee::getSalar, Integer::sum));
averagingIntDouble计算流中元素Integer属性的平均值double avg= list.stream().collect(Collectors.averagingInt(Employee::getSalary));
maxByOptional根据比较器选择最大值Optional max= list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary)));
minByOptional根据比较器选择最小值Optional min = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary)));
summarizingIntIntSummaryStatistics收集流中Integer属性的统计值。 如:平均值Int SummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Employee::getSalary));
collectingAndThen转换函数返回的类型包裹另一个收集器,对其结 果转换函数int how= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));

参加链接:Java8新特性之Stream流详细总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值