1.概念

流提供了一种让我们可以在比集合更高概念级别上指定计算的数据视图。通过使用流,我们可以说明想要完成什么任务,而不是说明如何去实现它。流遵守的是“做什么而非做什么”的原则来处理集合。
我们来举个例子,来说明流的具体作用。

老大让小路和小溪计算一下数组 [1,2,3,4,5,6,7] 的和

小路使用常规方法的解决方案如下:

  1. @Test
  2. public void lulu(){
  3. int[] array ={1,2,3,4,5,6,7};
  4. int sum = 0;
  5. for(int temp : array){
  6. sum += temp;
  7. }
  8. System.out.println(sum);
  9. }

小溪使用流的解决方案如下:

  1. @Test
  2. public void xixi(){
  3. int[] array ={1,2,3,4,5,6,7};
  4. int sum = Arrays.stream(array).sum();
  5. System.out.println(sum);
  6. }

总结一下: 小路的常规方法:需要告诉程序,遍历数组—>每遍历一个值,将其相加到sum中。 小溪的流方法:仅仅告诉程序说: 我要计算这个数组的值。

这就是流的原则:仅仅告诉程序,我要干什么或不干什么,不用告诉程序该怎么做。这就是所谓的 “做什么而非做什么”原则

2.流的创建

既然流如此的方便,那么我们该如何创建流呢?

2.1集合.stream()方式

这种方式是最最常用的方式,没有之一。
案例1:ArrayList集合转换成流

  1. @Test
  2. public void createStream(){
  3. List<String> list = Arrays.asList("lulu", "xixi", "amei");
  4. Stream<String> stream = list.stream();
  5. }

案例2:ArrayList<自定义类>集合转换成流

  1. @Test
  2. public void createStream(){
  3. List<Person> people = Arrays.asList(new Person(1L, "路路", "男"),
  4. new Person(2L, "阿美", "女"));
  5. Stream<Person> stream = people.stream();
  6. }

2.2Stream.of(T…values)

这种方式用的比较少,项目中不常用

  1. @Test
  2. public void createStream(){
  3. Stream<String> stream = Stream.of("露露", "阿美");
  4. }

2.3Arrays.stream(数组)

  1. @Test
  2. public void createStream(){
  3. int[] array ={1,2,3,4,5,6,7};
  4. IntStream stream = Arrays.stream(array);
  5. long[] array2={1L,2L,3L};
  6. LongStream stream1 = Arrays.stream(array2);
  7. double[] array3= {1.0,2.0,3.0};
  8. DoubleStream stream2 = Arrays.stream(array3);
  9. Person[] array4= {new Person(1L,"阿美","女"),
  10. new Person(2L,"露露","男")};
  11. Stream<Person> stream3 = Arrays.stream(array4);
  12. }

注:这里如果是 基本数据类型的数组 只能 使用 int / long / double 其余的基本数据类型数组无法创建流。 并且我们可以看到int / long 和double 创建出来的流 也和自定义类创建的流不一样。 Stream<> —— IntStream

以下的写法是错误的,char /boolean 等基本数据类型无法这样

  1. @Test
  2. public void createStream(){
  3. char[] array ={'1','2','3','4','5','6','7'};
  4. Arrays.stream(array);
  5. }

2.4Files.lines(Path path)

这个方法可厉害了。
案例:有一个文件a.txt,读取文件中的内容,我们可以使用以下方式
文件内容:
image.png

  1. @Test
  2. public void createStream() throws IOException {
  3. Stream<String> lines = Files.lines(Paths.get("a.txt"));
  4. lines.forEach(System.out::println);
  5. }

上面的读取文件是不是很nice。

3.流的操作

3.1过滤操作(filter)

过滤操作目的是留下集合中符合要求的元素
案例1:筛选出所有大于10的数字

  1. @Test
  2. public void createStream(){
  3. List<Integer> list =new ArrayList<>();
  4. list.add(1);
  5. list.add(2);
  6. list.add(12);
  7. list.add(13);
  8. list.stream().filter(x->x>10).forEach(System.out::println);
  9. }

案例2:筛选出所有性别为女性的信息

  1. @Test
  2. public void createStream(){
  3. List<Person> list =new ArrayList<>();
  4. list.add(new Person(1L,"阿美","女"));
  5. list.add(new Person(2L,"路路","男"));
  6. list.add(new Person(3L,"露露","女"));
  7. list.add(new Person(4L,"华华","男"));
  8. list.stream().filter(x -> x.getSex().equals("女")).forEach(System.out::println);
  9. }

filter中返回为true 的元素 都是要保留下来的

3.2 映射(map)

就是一个对象集合,我只想取其中某个或某些字段
案例:取人的所有名字并打印

  1. @Test
  2. public void createStream(){
  3. List<Person> list =new ArrayList<>();
  4. list.add(new Person(1L,"阿美","女"));
  5. list.add(new Person(2L,"路路","男"));
  6. list.add(new Person(3L,"露露","女"));
  7. list.add(new Person(4L,"华华","男"));
  8. list.stream().map(x->x.getName()).forEach(System.out::println);
  9. }

3.3 扁平化映射(flatmap)

  1. @Test
  2. public void createStream(){
  3. List<Person> list =new ArrayList<>();
  4. list.add(new Person(1L,"阿美","女"));
  5. list.add(new Person(2L,"路路","男"));
  6. list.add(new Person(3L,"露露","女"));
  7. list.add(new Person(4L,"华华","男"));
  8. Stream<Stream<Long>> streamStream = list.stream().map(x -> Stream.of(x.getId()));
  9. Stream<Long> longStream = list.stream().flatMap(x -> Stream.of(x.getId()));
  10. }

扁平化映射我也解释不清楚,但是看我分别写 map 和 flateMap得到到最终结果不一样,就大概可以悟出一些道理出来。。
对于map来说: 我返回的每个元素是个 Stream 元素 ,于是最后的流就是 Stream>
对于flatMap来说: 返回的还是Stream元素, 但是最后得到的结果是这样 Stream
仔细对比也是能明白其中的道理的。。。。

3.4排序 (sorted)

在做项目中可能会有排序的需求,一般我们会在数据库中排序,但是有时也会在代码中针对集合排序

案例1: 依据人的编号降序

对于自定义对象流调用sorted会进行自然排序, 也就是自定义类必须实现Comparable接口

  1. @Test
  2. public void createStream(){
  3. List<Person> list =new ArrayList<>();
  4. list.add(new Person(1L,"阿美","女"));
  5. list.add(new Person(2L,"路路","男"));
  6. list.add(new Person(3L,"露露","女"));
  7. list.add(new Person(4L,"华华","男"));
  8. list.stream().sorted().forEach(System.out::println);
  9. }
  10. java.lang.ClassCastException: com.gao.domain.Person cannot be cast to java.lang.Comparable

我们要依据编号排序,可以看到sorted中可以传入一个Coparator,这个类在下次有机会一定写写它的用法。

  1. @Test
  2. public void createStream(){
  3. List<Person> list =new ArrayList<>();
  4. list.add(new Person(1L,"阿美","女"));
  5. list.add(new Person(2L,"路路","男"));
  6. list.add(new Person(3L,"露露","女"));
  7. list.add(new Person(4L,"华华","男"));
  8. list.stream().sorted(Comparator.comparingLong(x->x.getId())).forEach(System.out::println);
  9. }

上面就是说,我要按照Id排序,且id是个long,按照long类型排序方法排序即可。但是long的默认排序是升序。

  1. @Test
  2. public void createStream(){
  3. List<Person> list =new ArrayList<>();
  4. list.add(new Person(1L,"阿美","女"));
  5. list.add(new Person(2L,"路路","男"));
  6. list.add(new Person(3L,"露露","女"));
  7. list.add(new Person(4L,"华华","男"));
  8. list.stream().sorted(Comparator.comparingLong(x->-x.getId())).forEach(System.out::println);
  9. }

于是来个小技巧,使用 - 号,这样就实现了逆序排序,类似整形的数字逆序排序 ,都是这样 玩。
或者 嘿嘿自定义比较器

  1. @Test
  2. public void createStream(){
  3. List<Person> list =new ArrayList<>();
  4. list.add(new Person(1L,"阿美","女"));
  5. list.add(new Person(2L,"路路","男"));
  6. list.add(new Person(3L,"露露","女"));
  7. list.add(new Person(4L,"华华","男"));
  8. list.stream().sorted((x,y)-> (int) (y.getId()-x.getId())).forEach(System.out::println);
  9. }

当然有时排序可能是下面的需求
案例2:按照编号升序,按照年龄降序

  1. @Test
  2. public void createStream(){
  3. List<Person> list =new ArrayList<>();
  4. list.add(new Person(1L,"阿美","女",1));
  5. list.add(new Person(2L,"路路","男",1));
  6. list.add(new Person(2L,"露露","女",10));
  7. list.add(new Person(4L,"华华","男",1));
  8. list.stream().sorted(Comparator.comparingLong(Person::getId)
  9. .thenComparing(Comparator.comparingInt(x->-x.getAge()))).forEach(System.out::println);
  10. }

嘿嘿 so easy。。

3.5其他操作

请查看 www.baidu.com

3.6基本数据类型对应的流

以上的操作时针对任意类型的流而言,那么别忘了还有3个基本数据类型的流他们有特殊的一些操作。。
int[] —- > IntStream
long[] ——> LongStream
double[] —-> DoubleStream

3.6.1 求和、最大、最小值、求平均

求和可能是对这些数组的最常用的操作,

  1. @Test
  2. public void createStream(){
  3. int[] array ={3,2,3,4,5,3};
  4. IntStream stream = Arrays.stream(array);
  5. int sum = stream.sum();
  6. System.out.println(sum);
  7. }
  8. @Test
  9. public void createStream(){
  10. long[] array ={3L,2L,3L,4L,5L,3L};
  11. LongStream stream = Arrays.stream(array);
  12. long sum = stream.sum();
  13. System.out.println(sum);
  14. }

类似 有 max(),min(),average();

3.6.2 summaryStatistics

  1. @Test
  2. public void createStream() {
  3. int[] array = {1,2,4,5,6,7,8,54};
  4. IntSummaryStatistics intSummaryStatistics = Arrays.stream(array).summaryStatistics();
  5. intSummaryStatistics.getAverage();
  6. intSummaryStatistics.getCount();
  7. intSummaryStatistics.getSum();
  8. intSummaryStatistics.getMin();
  9. intSummaryStatistics.getMax();
  10. double[] array2 ={0.1,0.2,2.3};
  11. DoubleSummaryStatistics doubleSummaryStatistics = Arrays.stream(array2).summaryStatistics();
  12. doubleSummaryStatistics.getAverage();
  13. doubleSummaryStatistics.getMax();
  14. doubleSummaryStatistics.getMin();
  15. }

将统计数字封装在 Int/Long/Double SummaryStatistics 中里面有最大、最小、和等统计参数。