Stream流简介和常用方法

简介

        在java8中,基于Lambda所带来的函数式编程, 引入了一个全新的Stream流概念,称为流式操作,用于简化集合和数组操作的API。

        这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。

        元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

Stream的目的: 用于简化集合和数组操作的API。

Stream流的思想核心: 流水线, 不是IO操作

Stream使用步骤:

  • 得到集合/数组Stream流对象

  • 把元素放上去(创建Stream对象)

  • 调用Stream类相关API进行数据加工处理

API方法分为两大类:

  • 中间操作的API 得到新的Stream

  • 终止操作的API 得到结果

Stream流的三类方法:

  1. 获取Stream流

    创建一条流水线,并把数据放到流水线上准备进行操作

  2. 中间方法

    流水线上的操作。一次操作完毕之后,还可以继续进行其他操作。

  3. 终结方法

    一个Stream流只能有一个终结方法,是流水线上的最后一个操作

Stream流使用注意事项:

  1. 在Stream流中无法直接修改集合、数组中的数据

  2. Stream流只能操作一次

  3. Stream方法返回的是新的流

  4. Stream不调用终止方法,中间的操作不会执行

Stream操作推荐链式调用,因为Stream流只能操作一次

获取Stream流

1.Collection集合得到Stream流

         Collection接口提供stream()

以下是三种集合获取Stream流的方法

//List集合
List<String> list1 = new ArrayList<>();
list1.add("abc");
list1.add("bbb");
list1.add("ccc");
list1.add("abc");

//得到Stream对象
Stream<String> stream = list1.stream();
//System.out.println(stream);

//Set集合
Set<String> set = new HashSet<>();
set.add("abc");
set.add("bbb");
set.add("ccc");
set.add("abc");
Stream<String> stream1 = set.stream();

//Map集合
Map<String,Integer> map = new HashMap<>();
map.put("abc",123);
map.put("bcd",345);

//得到key集合的stream
Stream<String> stream2 = map.keySet().stream();

//得到Entry的stream
Stream<Map.Entry<String, Integer>> stream3 = map.entrySet().stream();
        

2.数组得到Stream对象

        Stream.of() 得到

        注意: Stream.of() 要求的数组必须是对象数组, 基本数据类型的数组不支持

  //数组的stream对象
Stream<Integer> stream4 = Stream.of(1, 2, 3, 4, 5, 6);

Integer[] arr = {2,4,6,8,10};
Stream<Integer> stream5 = Stream.of(arr);

/*
 int[] arr1 = {1,2,3,4,5};
 Stream<int[]> arr11 = Stream.of(arr1); //把int[]数组作为一个元素, 错误的
 */

Stream流常用方法

  • 终结方法:返回值类型不再是 Stream 类型的方法,不再支持链式调用。终结方法包括 count()forEach() 方法;

  • 中间方法:又叫函数拼接方法。值返回值类型仍然是 Stream 类型的方法,支持链式调用(除了终结方法外,其与方法均为中间方法)

常用方法:

 1.forEach

        

void forEach(Consumer<? super T> action);

        forEach() 方法用来遍历流中的数据,是一个终结方法。该方法接收一个 Consumer 接口函数,会将每一个流元素交给该函数进行处理。示例如下

  List<String> arr = new ArrayList<>();
        Collections.addAll(arr,"小A","小B","小C","小D","小F");
        //forEach遍历
        // 未简写
        //  arr.forEach((String s)->{
        //  System.out.println(s);
        //  } );
        //简写1
        //arr.forEach(s -> System.out.println(s));
        //简写2
       arr.forEach(System.out::println);

2.filter

        

Stream<T> filter(Predicate<? super T> predicate);

        filter() 方法,用于过滤数据,返回符合过滤条件的数据,是一个非终结方法。我们可以通过 filter() 方法将一个流转换成另一个子集流。该接口接收一个 Predicate 函数式接口参数(可以是一个 Lambda 或 方法引用) 作为筛选条件。示例如下:查找名字长度大于2的元素

List<String> list = new ArrayList<>();
Collections.addAll(list,"张三","王乐乐","田亮","李四","王五");
//filter()过滤,返回名字长度大于2的元素
list.stream().filter(str->str.length()>2).forEach(System.out::println);

3.count

long count()

count() 方法,用来统计集合中的元素个数,是一个终结方法。该方法返回一个 long 值代表元素个数,示例如下:

List<String> list = new ArrayList<>();
Collections.addAll(list,"张三","王乐乐","田亮","李四","王五");
long count = list.stream().count();
System.out.println("集合个数:"+count);

4.limit

Stream<T> limit(long maxSize);

        limit() 方法,用来对 Stream 流中的数据进行截取,只取用前 n 个,是一个非终结方法。参数是一个 long 型,如果集合当前长度大于参数则进行截取,否则不进行操作。因为 limit() 是一个非终结方法,所以必须调用终止方法。示例如下:

 List<String> list = new ArrayList<>();
Collections.addAll(list,"张三","王乐乐","田亮","李四","王五");
//截取前面三个元素
list.stream().limit(3).forEach(System.out::println);

5.skip

 Stream<T> skip(long n);

如果希望跳过前几个元素,取后面的元素,则可以使用 skip()方法,获取一个截取之后的新流,它是一个非终结方法。参数是一个 long 型,如果 Stream 流的当前长度大于 n,则跳过前 n 个,否则将会得到一个长度为 0 的空流。因为 skip() 是一个非终结方法,所以必须调用终止方法。示例如下:

List<String> list = new ArrayList<>();
Collections.addAll(list,"张三","王乐乐","田亮","李四","王五");
list.stream().skip(3).forEach(System.out::println);

6.sorted

//根据元素的自然规律排序
Stream<T> sorted();
//根据比较器指定的规则排序
Stream<T> sorted(Comparator<? super T> comparator);

因为 sorted() 方法是一个非终结方法,所以必须调用终止方法。

场景:

  1. sorted() 方法:按照自然规律,默认为升序排序。

  2. sorted(Comparator comparator) 方法,按照指定的比较器规则排序(以降序为例)。 示例如下

        //sorted():根据元素的自然规律排序
        Stream<Integer> stream01 = Stream.of(66,33,11,55);
        stream01.sorted().forEach(System.out::println);

        //sorted(Comparator<? super T> comparator):根据比较器规则降序排序
        Stream<Integer> stream02 = Stream.of(66,33,11,55);
        stream02 .sorted((i1,i2)-> i2-i1).forEach(System.out::println);

7.distint

Stream<T> distinct();

distinct() 方法,可以用来去除重复数据。因为 distinct() 方法是一个非终结方法,所以必须调用终止方法。

去除重复数据,此处有几种情况:①基本类型去重 ②String类型去重 ③引用类型去重(对象去重)

①②使用 distinct() 方法可以直接去重 ③对象类型需要重写 equals() 和 hasCode() 方法,使用 distinct() 方法才能去重成功。

        //基本类型去重
        Stream<Integer> stream01 = Stream.of(66,33,11,55,33,22,55,66);
        stream01 .distinct().forEach(System.out::println);

        //字符串去重
        Stream<String> stream02 = Stream.of("AA","BB","AA");
        stream02.distinct().forEach(System.out::println);

 8.concat

 public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
            --> 合并两个流

concat() 方法,可以将两个Stream流合并成一个流进行返回。如果是三个流,则需要两两合并,不能一次性合并三个流。concat() 方法是 Stream 接口的静态方法,我们可以直接使用【类名.方法名】调用。

注意:concat() 方法此处接收的是 Stream 类型,不能接收 IntStream 等类型。concat() 是一个静态方法,与 java.lang.String 中的 concat() 方法是不同的。

        //concat()方法
        Stream<Integer> aStream = Stream.of(1, 2, 3);
        Stream<Integer> bStream = Stream.of(4, 5, 6);
 
        Stream<Integer> concatStream = Stream.concat(aStream, bStream);
        concatStream.forEach(System.out::println);

9.reduce

//1.
Optional<T> reduce(BinaryOperator<T> accumulator);    //无默认值(可能为空,所以返回 Optional<T> 类型)
//2.
T reduce(T identity, BinaryOperator<T> accumulator);    //有默认值(所以返回类型为 T)
 
//T identity:默认值
//BinaryOperator<T> accumulator:对数据进行处理的方式
 
//提醒:BinaryOperator<T> 接口继承自 BiFunction,实际上使用的还是 BiFunction<T, U, R>中的apply()方法,因为apply(T t,U u)有两个参数,
//所以 在下面你会看到 BinaryOperator<T> accumulator 这个参数传递的是(x,y) 两个参数。

//3.
<U> U reduce(U identity,
                 BiFunction<U, ? super T, U> accumulator,
                 BinaryOperator<U> combiner);

 需要将 Sream 流中的所有数据,归纳得到一个数据的情况,可以使用 reduce() 方法。如果需要对 Stream 流中的数据进行求和操作、求最大/最小值等(都是归纳为一个数据的情况),此处就可以用到 reduce() 方法。示例如下

        //reduce():求和操作
        Stream<Integer> stream01 = Stream.of(4,3,5,9);
        Integer sum = stream01.reduce(0,(a,b)-> a + b);
        System.out.println("求和:"+sum);
 
        //reduce():求最大值操作
        Stream<Integer> stream01 = Stream.of(4,3,5,9);
        Integer max= stream01.reduce(0,(x,y)-> x > y ? x : y);
        System.out.println("最大值为:"+max);
 
        //reduce():求最小值操作
        Stream<Integer> stream01 = Stream.of(4,3,5,9);
        Optional<Integer> max= stream01.reduce((x, y)-> x < y ? x : y);
        System.out.println("最小值为:"+max.get());

10.map

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

map() 方法,可以将流中的元素映射到另一个流中。即:可以将一种类型的流转换为另一种类型的流,map() 方法是一个非终结方法。该接口需要一个 Function 函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。

这个方法有三个对于原始类型的变种方法,分别是:mapToInt,mapToLong 和 mapToDouble。这三个方法也比较好理解,比如 mapToInt 就是把原始 Stream 转换成一个新的 Stream,这个新生成的 Stream 中的元素都是 int 类型。之所以会有这样三个变种方法,可以免除自动装箱/拆箱的额外消耗。

IntStream mapToInt(ToIntFunction<? super T> mapper);
LongStream mapToLong(ToLongFunction<? super T> mapper);
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);

因为 map() 方法是一个非终结方法,所以必须调用终止方法。通过如下示例,使用 map() 方法,通过方法引用,便将字符串类型转换成了 Integer 类型。示例如下:

        List<String> list = new ArrayList<>();
        Collections.addAll(list,"11","22","33","44","55");
        //通过map()方法,可以将String类型的流转换为int类型的流
        /*list.stream().map((String str)->{
              return Integer.parseInt(str);
          }).forEach((Integer num) -> {
              System.out.println(num);
          });*/
        //简化:
        //list.stream().map(str->Integer.parseInt(str)).forEach(str->System.out.println(str));
        //简化后:
        list.stream().map(Integer::parseInt).forEach(System.out::println);

11.flatMap

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

        flatMap 的使用,同 map 类似。map只是一维 1对1 的映射,返回的是指定的类型;而flatMap返回的则还是一个Stream流,可以对其进行进一步操作。(区别:map返回的是指定类型(比如int),而flatMap返回的还是一个Stream流)

        假如你的集合流中包含子集合(或者需要更深进一步操作),那么使用 flatMap 可以返回该子集合的集合流。示例代码如下所示:

public class Province {
 
    private String name;
 
    private List<String> city;
 
    //get/set 方法
}
public class FlatMapDemo{
    public static void main(String[] args) {
 
        List<Province> provinceList = new ArrayList<>();
 
        List<String> bjCityList = new ArrayList<>();
        bjCityList.add("海淀");
        bjCityList.add("朝阳");
 
        List<String> hnCityList = new ArrayList<>();
        hnCityList.add("长沙");
        hnCityList.add("株洲");
        hnCityList.add("张家界");
 
        Province bjProvince = new Province();
        bjProvince.setName("北京");
        bjProvince.setCity(bjCityList);
        provinceList.add(bjProvince);
 
        Province hnProvince = new Province();
        hnProvince.setName("湖南");
        hnProvince.setCity(hnCityList);
        provinceList.add(hnProvince);
 
        //使用map,需要多次forEach
        provinceList.stream().map(str->str.getCity()).forEach(cityList -> cityList.forEach(System.out::println));
 
        System.out.println("----------");
 
        //使用 flatMap
        provinceList.stream().flatMap(str->str.getCity().stream().filter(city->city.length()>2)).forEach(System.out::println);
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值