Java8新特性之Stream API

Stream API

1、Stream流式思想概述

注意:Stream和IO流(InputStream/OutputStream)没有任何关系。

Stream流式思想类似于工厂车间的"生产流水线",Stream流不是一种数据结构,不保存数据,而是对数据进行加工处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。

Stream API能让我们快速完成许多复杂的操作,如筛选、切片、映射、查找、去除重复、统计、匹配和归约。

2、Stream流获取方式

2.1 根据Collection获取

​ 首先,java.util.Collection接口中加入了default方法stream,也就是说collection接口下的所有的实现都可以通过Stream方法来获取Stream流。

public static void main(String[] args) {
    List<string> list = new ArrayList<>();
    list.stream();
    Set<string> set = new Hashset<>();
    set.stream();
    vector vector = new vector();
    vector.stream();
}

但是Map接口并没有实现Collection接口,这时我们可以根据Map获取对应的key value的集合。

public static void main(String[] args) {
    HashMap<Object, Object> map = new HashMap<>();
    Stream<Object> stream = map.keySet().stream();//key
    Stream<Object> stream1 = map.values().stream();//value
    Stream<Map.Entry<Object, Object>> stream2 = map.entrySet().stream();//entry
}

2.2 根据Stream的of方法

​ 在实际开发中我们不可避免的还是会操作到数组中的数据,由于数组对象不可能添加默认方法,所以Stream接口中提供了静态方法of。

public static void main(String[] args) {
    Stream<String> a1 = Stream.of("a1", "a2", "a3");
    String[] arr1= {"aa","bb","cc"};
    Stream<String> arr11 = Stream.of(arr1);
    Integer[] arr2 = {1,2,3,4};
    Stream<Integer> arr21 = Stream.of(arr2);
    arr21.forEach(System.out::println);

    //注意,基本数据类型的数组是不行的
    int[] arr3 = {1,2,3,4};
    Stream.of(arr3).forEach(System.out::println);
}

3、Stream常用方法介绍

Stream常用方法

Stream流模型的操作很丰富,这里介绍一些常用的API。这些方法可以被分成两种:

方法名方法作用返回值类型方法种类
count统计个数long终结
forEach逐级处理void终结
filter过滤Stream函数拼接
limit取用前几个Stream函数拼接
skip跳过前几个Stream函数拼接
map映射Stream函数拼接
concat组合Stream函数拼接

终结方法: 返回值类型不再是Stream类型的方法,不再支持链式调用。本小节中,终结方法包括count和forEach方法。

非终结方法: 返回值类型仍然是Stream类型的方法,支持链式调用。(除了终结方法外,区域方法均为非终结方法)

Stream注意事项(重要)

  1. Stream只能操作一次
  2. Stream方法返回的是新的流
  3. Stream不调用终结方法,中间的操作不会执行

3.1 forEach

forEach用来遍历流中的数据的

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

该方法接受一个Consumer接口,会将每一个流元素交给函数处理

public static void main(String[] args) {
    Stream.of("a1", "a2", "a3").forEach(System.out::println);;
}

3.2 count

Stream流中的count方法用来统计其中的元素个数

long count();

该方法返回一个long值,代表元素的个数

public static void main(String[] args) {
    System.out.println(Stream.of("a1", "a2", "a3").count());
}

3.3 filter

filter方法的作用是用来过滤数据的。返回符合条件的数据。可以通过filter方法将一个流转换成另一个子集流。

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

该接口接受一个Predicate函数式接口参数作为筛选条件

public static void main(String[] args) {
    Stream.of("a1", "a2", "a3", "b1", "b2", "b3")
            .filter((s) -> s.contains("b"))
            .forEach(System.out::println);
}

输出:

b1
b2
b3

3.4 limit

limit方法可以对流进行截取处理,只取前n个数据

Stream<T> limit(long maxSize);

参数是一个long类型的数值,如果集合当前长度大于参数就进行截取,否则不操作:

public static void main(String[] args) {
    Stream.of("a1", "a2", "a3", "b1", "b2", "b3")
            .limit(5)
            .forEach(System.out::println);
}

输出:

a1
a2
a3
b1
b2

3.5 skip

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

Stream<T> skip(long n);

操作:

public static void main(String[] args) {
        Stream.of("a1", "a2", "a3", "b1", "b2", "b3")
                .skip(3)
                .forEach(System.out::println);
    }

输出:

b1
b2
b3

3.6 map

如果我们需要将流中的元素映射到另一个流中,可以使用map方法:

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

在这里插入图片描述

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

public static void main(String[] args) {
        Stream.of("1", "2", "3", "4", "5", "6")
//                .map(msg -> Integer.parseInt(msg))效果等同于下面
                .map(Integer::parseInt)
                .forEach(System.out::println);
    }

3.7 sorted

如果需要将数据排序,可以使用sorted方法:

Stream<T> sorted();

在使用的时候可以根据自然规则排序,也可以通过比较器来指定对应的排序规则

public static void main(String[] args) {
        Stream.of("1", "22", "23", "2", "4", "6")
//                .map(msg -> Integer.parseInt(msg))效果等同于下面
                .map(Integer::parseInt)
//                .sorted()//根据数据的自然顺序排序
                .sorted((o1,o2) -> o2-o1)//根据比较器指定排序规则
                .forEach(System.out::println);
    }

3.8 distinct

如果要去掉重复数据,可以使用distinct方法:

Stream<T> distinct();

在这里插入图片描述

Stream流中的distinct方法对于基本数据类型是可以直接去重的,对于自定义类型,我们需要去重写hasCode和equals方法来移除重复元素。

public static void main(String[] args) {
        Stream.of("1", "22", "22", "2", "4", "6")
//                .map(msg -> Integer.parseInt(msg))效果等同于下面
                .map(Integer::parseInt)
//                .sorted()//根据数据的自然顺序排序
                .sorted((o1,o2) -> o2-o1)//根据比较器指定排序规则
                .distinct()//去掉重复的记录
                .forEach(System.out::println);
    }

3.9 reduce方法

如果需要将所有数据归纳得到一个数据,可以使用reduce方法

T reduce(T identity, BinaryOperator<T> acumulator);

使用:

public static void main(String[] args) {
        Integer sum = Stream.of(1, 2, 3, 4)
                //identity默认值
                //第一次的时候会将默认值赋给x
                //之后每次会将上一次的操作结果赋给x y就是每次从数据中获取的元素
                .reduce(0, (x, y) -> {
                    System.out.println("x=" + x + ",y=" + y);
                    return x + y;
                });
        System.out.println(sum);
        //获取最大值
        Integer max = Stream.of(1, 2, 3, 4)
                .reduce(0, (x, y) -> {
                    return x > y ? x : y;
                });
        System.out.println("最大值:"+max);
    }

结果:

x=0,y=1
x=1,y=2
x=3,y=3
x=6,y=4
10
最大值:4

3.10 map和reduce的组合

在实际开发中,我们会将map和reduce一块使用

public static void main(String[] args) {
        Integer sumAge = Stream.of(
                        new Person("张三", 18),
                        new Person("李四", 19),
                        new Person("王五", 20),
                        new Person("赵六", 21)
//        ).map(p->p.getAge)
                ).map(Person::getAge)//实现数据类型转换,符合reduce对数据的要求
//                .reduce(0,(x,y)->x+y);
                .reduce(0, Integer::sum);//reduce实现数据的处理
        System.out.println(sumAge);
    }

3.11 mapTolnt

如果需要将Stream中的Integer类型转换为int类型,可以使用mapToInt方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BW7JlAZq-1658798400699)(C:\Users\yfzheng.ARCVIDEO\AppData\Roaming\Typora\typora-user-images\image-20220726090439598.png)]

使用:

public static void main(String[] args) {
    //Integer比int占用的内存多很多,在Stream流操作中会自动装箱和拆箱操作
    Integer[] arr = {1,2,3,4,5,6,7,8};
    Stream.of(arr)
            .filter(i->i>0)
            .forEach(System.out::println);
    //为了提高代码的效率,我们可以先将流中Integer数据转换为int数据,然后再操作
    IntStream intStream = Stream.of(arr)
            .mapToInt(Integer::intValue);
    intStream.filter(i->i>3)
            .forEach(System.out::println);
}

3.12 concat

如果两个流,希望合并成为一个流,那么可以使用Stream接口的静态方法concat

public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) {
    Objects.requireNonNull(a);
    Objects.requireNonNull(b);

    @SuppressWarnings("unchecked")
    Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>(
            (Spliterator<T>) a.spliterator(), (Spliterator<T>) b.spliterator());
    Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());
    return stream.onClose(Streams.composedClose(a, b));
}

使用:

public static void main(String[] args) {
    Stream<String> stream1 = Stream.of("a", "b", "c");
    Stream<String> stream2 = Stream.of("x", "y", "z");
    //通过concat方法将两个流合并成为一个新的流
    Stream.concat(stream1,stream2).forEach(System.out::print);
}

输出:

abcxyz

4、例子

统计个数:list.stream().count

去重: .distinct()

遍历: .forEach(a-> System.out.println(a))

过滤: .filter

限制个数: .limit

跳过: .skip

map映射: .map(a->a+" ")

连接多个数组:Ints.concat()

list转字符串:Joiner.on(“,”).join()

map转字符串:Joiner.on(" , “).withKeyValueSeparator(” = ").join()

list转string:跳过 null:Joiner.on(“,”).skipNulls().join()

list转string:null变为其他值:Joiner.on(“,”).useForNull(“”).join()

根据-切割,将string转为list:Splitter.on(“-”).trimResults().splitToList()

string转为map:Splitter.on(“,”).withKeyValueSeparator(“=”).split()

多个字符进行分割:Splitter.onPattern(“[.|,]”)

每隔n字符进行分割:Splitter.fixedLength(n).splitToList()

取两个集合的并集并去重:listAll.stream().distinct().collect(toList())

List集合去重:list.stream().distinct().collect(toList())

List集合去重:List.stream().collect(collectingAndThen(toCollection(() -> new TreeSet<>(Comparator.comparing(User::getName))), ArrayList::new))

匹配: anyMatch(),只要有一个元素匹配传入的条件,就返回 true。
allMatch(),只有有一个元素不匹配传入的条件,就返回 false;如果全部匹配,则返回 true。
noneMatch(),只要有一个元素匹配传入的条件,就返回 false;如果全部不匹配,则返回 true。

5、Stream结果收集

5.1 结果收集到集合中

操作:

public static void main(String[] args) {
        /**
         * Stream结果收集
         * 收集到集合中
         */
        //收集到List集合中
//        Stream<String> stream = Stream.of("aa", "bb", "cc");
        List<String> list = Stream.of("aa", "bb", "cc", "aa")
                .collect(Collectors.toList());
        System.out.println(list);
        //收集到Set集合中
        Set<String> set = Stream.of("aa", "bb", "cc", "aa")
                .collect(Collectors.toSet());
        System.out.println(set);

        //如果需要获取的类型为具体的实现,比如:ArrayList HashSet
        ArrayList<String> arrayList = Stream.of("aa", "bb", "cc", "aa")
//                .collect(Collectors.toCollection(() -> new ArrayList<>()));
        .collect(Collectors.toCollection(ArrayList::new));
        System.out.println(arrayList);
        HashSet<String> hashSet = Stream.of("aa", "bb", "cc", "aa")
                .collect(Collectors.toCollection(HashSet::new));
        System.out.println(hashSet);
    }

输出:

[aa, bb, cc, aa]
[aa, bb, cc]
[aa, bb, cc, aa]
[aa, bb, cc]

5.2 结果收集到数组中

Stream中提供了toArray方法来将结果放到一个数组中,返回值类型是Object[],如果我们要指定返回的类型,那么可以使用另一个重载的toArry(IntFunction f)方法

操作:

public static void main(String[] args) {
    /**
     * Stream结果收集收集到数组中
     */
    Object[] objects = Stream.of("aa", "bb", "cc", "aa")
            .toArray();//返回的数组中的元素是Object类型
    System.out.println(Arrays.toString(objects));
    //如果我们需要指定返回的数组中的元素类型
    String[] strings = Stream.of("aa", "bb", "cc", "aa")
            .toArray(String[]::new);
    System.out.println(Arrays.toString(strings));
}

输出:

[aa, bb, cc, aa]
[aa, bb, cc, aa]

5.3 对流中的数据做分区操作

Collectors.partitioningBy会根据值是否为true,把集合的数据分割为两个列表,一个true列表,一个false列表
在这里插入图片描述

5.4 对流中的数据做拼接

Collector.joining会根据指定的连接符,将所有的元素连接成一个字符串

6、 并行的Stream流

6.1 串行的Stream流

我们前面使用的Stream流都是串行,也就是在一个线程上面执行

public static void main(String[] args) {
    Stream.of(5,4,1,2,5,5,8)
            .filter(s->{
                System.out.println(Thread.currentThread()+""+s);
                return s>3;
            }).count();
}

输出:

Thread[main,5,main]5
Thread[main,5,main]4
Thread[main,5,main]1
Thread[main,5,main]2
Thread[main,5,main]5
Thread[main,5,main]5
Thread[main,5,main]8

6.2 并行流

parallelStream其实就是一个并行执行的流,它通过默认的ForkJoinPool,可以提高多线程任务的速度。

获取并行流:

public static void main(String[] args) {
    /**
     * 功能描述:获取并行流的两种方式
     */
    ArrayList<Integer> list = new ArrayList<>();
    //通过list接口直接获取并行流
    Stream<Integer> integerStream = list.parallelStream();
    //将已有的串行流转换为并行流
    Stream<Integer> parallel = Stream.of(1, 2, 3).parallel();
}

并行流操作:

public static void main(String[] args) {
        Stream.of(1, 2, 3, 4, 1)
                .parallel()//将流转换为并发流,Stream处理的时候就会通过多线程处理
                .filter(s -> {
                    System.out.println(Thread.currentThread() + "s=" + s);
                    return s > 2;
                }).count();
    }

输出:

Thread[main,5,main]s=3
Thread[main,5,main]s=1
Thread[main,5,main]s=4
Thread[ForkJoinPool.commonPool-worker-1,5,main]s=2
Thread[main,5,main]s=1

Stream并行处理的过程会分而治之,也就是将一个大的任务切分成了多个小任务,这表示每个任务都是线程操作。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShanHai山海

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值