JAVA8学习6-流(2)

6.5 Stream 流的转换方法

  1. Object[] toArray() 返回一个Object数组
  2. A[] toArray(IntFunction<A[]> generator) 返回一个 指定类型的数组
  3. collect(Collectors.toList()) 将 Stream 流转换为 list 集合
  4. collect() 系列方法
  5. Stream.generate() 创建一个无限流,一般用于生成随机数等方法。需要配合findFirst获取他第一个函数,值为 Optional 。
  6. Stream.iterate(T,UnaryOperator<T,T>) 无限流,函数通过对第一个参数的操作,生成无线流,需要和 limit 配合使用,限制生成的个数

写几个小例子:

package cn.zxhysy.jdk8.steam;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import java.util.stream.Collectors;

public class StreamTest4 {

  public static void main(String[] args) {
    Stream<String> stream = Stream.of("hellow", "world", "helloworld");
    // toArray 将stream流转为一个数组,接受一个 IntFunction<R>(整数函数)返回一个R类型
    // length 就是数组长度
    String[] strings = stream.toArray(length -> {
      System.out.println(length); //3
      return new String[length];
    });
    // String[] strings = stream.toArray(String::new)
    // 将流转换为集合
    List<String> list = stream.collect(Collectors.toList());
    Arrays.asList(strings).forEach(System.out::println);
  }
}

使用 collect 方法实现转成 ArrayList

R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner)

这里我们先看下 Collectors.toList() 方法

public static <T>
Collector<T, ?, List<T>> toList() {
    return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
                               (left, right) -> { left.addAll(right); return left; },
                               CH_ID);
}

java8 源码中的示例

List<String> asList = stringStream.collect(ArrayList::new, ArrayList::add,ArrayList::addAll);                     

第一各参数:返回的数据类型
第二个参数:是将每一个对象添加到一个集合里面
第三个参数:是将list集合添加起来,我测试打印数据,但数据没效果,theList1.addAll() 没写也不影响结果, 别人是这么介绍了:第二个参数每次执行都会产生一个新的list,然后把 每个list的数据添加起来。

package cn.zxhysy.jdk8.steam;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class StreamTest4 {

    public static void main(String[] args) {

        Stream<String> stream = Stream.of("hellow", "world", "helloworld");
        List<String> list = stream.collect(() -> new ArrayList(),
                (theList, item) -> {
                    System.out.println("-------");
                    System.out.println("前:theList" + theList);
                    theList.add(item);
                    System.out.println("后:theList" + theList);
                },
                (theList1, theList2) -> {
                  //这里就算注释了,打印的结果都不变
                  // 这条语句永远不会输出
//                    System.out.println("1--------");
//                    theList1.addAll(theList2);
        });
        //List<String> list = stream.collect(ArrayList:: new, List::add, List::addAll);
        list.forEach(System.out::println);
    }
}

Collectors.toCollection(集合类型):

//Collectors.toCollection(这里可以写你想转换的集合类型)
stream.collect(Collectors.toCollection(ArrayList::new));

Collectors.toSet() 返回Set集合
Collectors.joining() 连接字符串 等等

创建流的其他方式:

package cn.zxhysy.jdk8.steam;

import java.util.UUID;
import java.util.stream.Stream;

public class StreamTest6 {

    public static void main(String[] args) {
        Stream<String> stream = Stream.generate(UUID.randomUUID()::toString);
        //stream.forEach(System.out::println);这个无限循环
        System.out.println(stream.findFirst().get());
        stream.findFirst().ifPresent(System.out::println);
        // iterate 生成无限串行流,必须无limit配合使用
        Stream.iterate(1, item -> item + 2).limit(6).forEach(System.out::println);
    }

}

6.6 Stream 流的常用操作方法

介绍流的操作前,先给出个问题:

1, 3, 5,7, 9,11

找出该流中大于2的元素,然后将每个元素乘以2,然后忽略掉流中的前两个元素,然后再去流中的前两个元素,最后求出流中元素的总和

Stream<Integer> stream = Stream.iterate(1, item -> item + 2).limit(6);
int sum = stream.filter(item -> item > 2)
            .mapToInt(item -> item * 2)
            .skip(2)
            .limit(2)
            .sum();
System.out.println(sum);//32

改下需求,最后求的是最小值:

OptionalInt min = stream.filter(item -> item > 2)
                .mapToInt(item -> item * 2)
                .skip(2)
                .limit(2)
                .min();
min.ifPresent(System.out::println);//14

可以看到,min() 或者 max() 方法,获取的值都是 Optional 类型,这都是为了防止 NPE 异常,而设计的。为什么sum() 返回的是 int, 这是因为求和时,默认值是0。

再假设,我们要同时获取最大值最小值以及总和,又需要怎么做。

IntSummaryStatistics summaryStatistics = stream.filter(item -> item > 2)
        .mapToInt(item -> item * 2)
        .skip(2)
        .limit(2)
        .summaryStatistics();
int min = summaryStatistics.getMin();
System.out.println(min);

返回的 IntSummaryStatistics 中,有对返回流结果的一系列处理,如最大值,最小值、总和等。

上面所用到的方法:

  1. filter(boolean) 过滤,通过断言(boolean值)判断,值为 false 被过滤
  2. mapToInt(item) 映射操作,对一个值进行操作,如加减乘除.类似的还有 map、mapToDouble,用 mapToInt 是为了防止 数据自动包装、拆装
  3. skip(int) 忽略,忽略几个元素,从1开始
  4. limit(int) 截取,截取几个元素,从1开始
  5. sum() 求和, 算术求和
  6. min()、max() 求最小值、最大值,返回 OptionalInt 类型
  7. summaryStatistics() 统计函数,返回 IntSummaryStatistics 流,里面包含对元素的数值处理方法,如 getMin、getMax 等等。
  8. forEach 遍历
  9. sort(compare) 排序,默认自然遍历
  10. flatMap(类型 -> return stream) 打平 (后面有例子单独介绍*****)

注意:

  1. 凡是流的中间操作返回的肯定是流,终止操作要么没有返回值,要么返回值是其他非流类型。
  2. 异常:流已经被操作了,流已经被关闭了。对于 流 其实与 IO 流 是同个道理的,一旦流被操作、被使用了、关闭了,就不能再使用这个流。如何避免,就需要在第一次调用的时候生成新的流,用新的流来操作。建议使用链式编程
  3. 流的中间操作都是懒加载的,只有终止操作(及早操作)出现,才会执行中间操作,试下下列代码执行结果
  4. 任何对无限流的操作必须在 limit 之后,不然会导致死循环,看下面第二例子
package cn.zxhysy.jdk8.steam;

import java.util.Arrays;
import java.util.List;

public class StreamTest7 {

	public static void main(String[] args) {

		List<String> list = Arrays.asList("hello", "world", "hello world");

// 这里的代码,运行起来是不会打印 test 的,
// 正确的说法是map,没有被执行
// 在 stream 中,中间操作是懒加载额
//        list.stream().map(item -> {
//            String result = item.substring(0, 1).toUpperCase() + item.substring(1);
//            System.out.println("test");
//            return result;
//        });
// 只有调用了 forEach 方法之后才有打印值
    list.stream().map(item -> {
      String result = item.substring(0, 1).toUpperCase() + item.substring(1);
      System.out.println("test");
      return result;
    }).forEach(System.out::println);
  }
}

stream 会将所有的中间操作全部存储起来后遇到终止操作时,再执行。

无限流死循环情况

package cn.zxhysy.jdk8.steam;

import java.util.stream.IntStream;

public class StreamTest8 {

    public static void main(String[] args) {
//        IntStream.iterate(0, i -> (i+1)%2)
//                .distinct().limit(6)
//                .forEach(System.out::println);
        // 上面的代码回无限循环下去
        // 因为 distinct 方法不知道 iterate 会无限循环重复两个值 0 1
        // 还不能走到limit 那一步就死循环了。
        // 正确的修改
        IntStream.iterate(0, i -> (i+1)%2)
                .limit(6)
                .distinct()
                .forEach(System.out::println);
    }
}

flatMap 把多个stream合成一个stream,就是把结果打平

下面给出一道去重的题目:

package cn.zxhysy.jdk8.steam;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamTest11 {

    public static void main(String[] args) {
        List<String> list = Arrays.asList("hello world", "world hello",
                "hello world hello", "hello welcome");
        // 找出集合中的所有单词,去重
        // 下面写法是错的, split 返回的是一个字符串数组所以 distinct 对字符串数组去重就没作用了
//        List<String[]> collect = list.stream().map(item -> item.split(" "))
//                .distinct().collect(Collectors.toList());
//        collect.forEach(item -> Arrays.asList(item).forEach(System.out::println));

        // 正确做法
        list.stream().map(item -> item.split(" "))
                .flatMap(Arrays::stream)
                .distinct().collect(Collectors.toList())
                .forEach(System.out::println);
    }

}

再给出一道题目:

list1 存放这招呼方式,list2 为每个人名,要求输出 给每个人打招呼。

package cn.zxhysy.jdk8.steam;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamTest12 {

    public static void main(String[] args) {
        List<String> list1 = Arrays.asList("Hi", "Hello", "你好");
        List<String> list2 = Arrays.asList("zhangsan", "lisi", "wangwu", "zhaoliu");
        // 给每个人前加招呼

        List<String> collect = list1.stream().
                flatMap(item -> list2.stream().map(item2 -> item + " " + item2))
                .collect(Collectors.toList());
        collect.forEach(System.out::println);
        // 输出 Hi zhangsan hi lisi....
    }
}

6.7 外部迭代和内部迭代

6.7.1 外部迭代

​ 在java8之前,我们都是有 for 或者 增强for等用来遍历数据集合,对数据进行操作。

List<Student> list = new ArrayList<>();
for(int i = 0; i < StudentList.size()++i){
  // todos
  if(xxx){
    list.add(student.get(i)); 
  }
}
Collections.sort(list, comparator);
for(Student s: list){
	sout(s.getName);
}

大量麻烦操作,需要我们处理,且可读性差。

6.7.2 内部迭代

​ 在java8之后,采用流的方式对数据进行一系列的操作,这里就不做介绍了。
与外部迭代相比,内部迭代采用的是描述性的语音,而不是命令是的操作。不仅简化了操作,还增强了代码的可阅读性。内部迭代采用的数据是源本身,而不会生成多余的数据来比较。内部迭代和外部迭代的主要区别就是操作对象不同,内部迭代操作的是流,外部迭代操作的是集合。
​ 集合和流的区别:集合关注的是数据与数据存储本身,流关注的是对数据所执行的计算

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值