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 list = new ArrayList<>();
    list.stream();
    Set set = new Hashset<>();
    set.stream();
    vector vector = new vector();
    vector.stream();
}

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

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

2.2 根据Stream的of方法

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

public static void main(String[] args) {
    Stream a1 = Stream.of("a1", "a2", "a3");
    String[] arr1= {"aa","bb","cc"};
    Stream arr11 = Stream.of(arr1);
    Integer[] arr2 = {1,2,3,4};
    Stream 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注意事项(重要)

Stream只能操作一次
Stream方法返回的是新的流
Stream不调用终结方法,中间的操作不会执行
3.1 forEach
forEach用来遍历流中的数据的
 

void forEach(Consumer action);

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

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

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

long count();
1
该方法返回一个long值,代表元素的个数

public static void main(String[] args) {
    System.out.println(Stream.of("a1", "a2", "a3").count());
}
1
2
3
3.3 filter
filter方法的作用是用来过滤数据的。返回符合条件的数据。可以通过filter方法将一个流转换成另一个子集流。

Stream filter(Predicate predicate);
1
该接口接受一个Predicate函数式接口参数作为筛选条件

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

b1
b2
b3
1
2
3
3.4 limit
limit方法可以对流进行截取处理,只取前n个数据

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

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

a1
a2
a3
b1
b2
1
2
3
4
5
3.5 skip
如果希望跳过前面几个元素,可以使用skip方法获取一个截取之后的新流。

Stream skip(long n);
1
操作:

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

b1
b2
b3
1
2
3
3.6 map
如果我们需要将流中的元素映射到另一个流中,可以使用map方法:

Stream map(Function 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 sorted();
1
在使用的时候可以根据自然规则排序,也可以通过比较器来指定对应的排序规则

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);
    }
1
2
3
4
5
6
7
8
3.8 distinct
如果要去掉重复数据,可以使用distinct方法:

Stream 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);
    }
1
2
3
4
5
6
7
8
9
3.9 reduce方法
如果需要将所有数据归纳得到一个数据,可以使用reduce方法

T reduce(T identity, BinaryOperator acumulator);
1
使用:

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);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
结果:

x=0,y=1
x=1,y=2
x=3,y=3
x=6,y=4
10
最大值:4
1
2
3
4
5
6
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);
    }
1
2
3
4
5
6
7
8
9
10
11
12
3.11 mapTolnt
如果需要将Stream中的Integer类型转换为int类型,可以使用mapToInt方法

 

 

使用:

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);
}
1
2
3
4
5
6
7
8
9
10
11
12
3.12 concat
如果两个流,希望合并成为一个流,那么可以使用Stream接口的静态方法concat

public static Stream concat(Stream a, Stream b) {
    Objects.requireNonNull(a);
    Objects.requireNonNull(b);

    @SuppressWarnings("unchecked")
    Spliterator split = new Streams.ConcatSpliterator.OfRef<>(
            (Spliterator) a.spliterator(), (Spliterator) b.spliterator());
    Stream stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());
    return stream.onClose(Streams.composedClose(a, b));
}
1
2
3
4
5
6
7
8
9
10
使用:

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

abcxyz
1
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 stream = Stream.of("aa", "bb", "cc");
        List list = Stream.of("aa", "bb", "cc", "aa")
                .collect(Collectors.toList());
        System.out.println(list);
        //收集到Set集合中
        Set set = Stream.of("aa", "bb", "cc", "aa")
                .collect(Collectors.toSet());
        System.out.println(set);

        //如果需要获取的类型为具体的实现,比如:ArrayList HashSet
        ArrayList arrayList = Stream.of("aa", "bb", "cc", "aa")
//                .collect(Collectors.toCollection(() -> new ArrayList<>()));
        .collect(Collectors.toCollection(ArrayList::new));
        System.out.println(arrayList);
        HashSet hashSet = Stream.of("aa", "bb", "cc", "aa")
                .collect(Collectors.toCollection(HashSet::new));
        System.out.println(hashSet);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
输出:

[aa, bb, cc, aa]
[aa, bb, cc]
[aa, bb, cc, aa]
[aa, bb, cc]
1
2
3
4
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));
}
1
2
3
4
5
6
7
8
9
10
11
12
输出:

[aa, bb, cc, aa]
[aa, bb, cc, aa]
1
2
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();
}
1
2
3
4
5
6
7
输出:

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
1
2
3
4
5
6
7
6.2 并行流
parallelStream其实就是一个并行执行的流,它通过默认的ForkJoinPool,可以提高多线程任务的速度。

获取并行流:

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

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();
    }
1
2
3
4
5
6
7
8
输出:

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
1
2
3
4
5

finish

你可能感兴趣的:(java,开发语言)