JAVA8之Stream API

1.StreamAPI可以做什么

StreamAPI为我们提供了强大的集合操作,同时StreamAPI操作简单,代码直观,容易上手。

2.Stream的操作步骤

List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7);

1.创建Stream

Stream<Integer> stream = integers.stream();

2.中间操作

注意:没有终止操作是不会执行中间操作的

Stream<Integer> integerStream = stream.filter(num -> {
  System.out.println("current num is " + num);
  return num > 2;
});

3.终止操作

List<Integer> collect = integerStream.collect(Collectors.toList());
System.out.println(collect);//[3, 4, 5, 6, 7]

综合成一条语句就是

List<Integer> collect = integers.stream().filter(num -> {
  System.out.println("current num is " + num);
  return num > 2;
}).collect(Collectors.toList());

3.常见的中间操作

3.1筛选和切片

  • filter:过滤出某些元素
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
//filter(num -> num > 2),此处是过滤出大于2的元素
List<Integer> collect = integers.stream().filter(num -> num > 2).collect(Collectors.toList());
System.out.println(collect);//[3, 4, 5, 6, 7]
  • limit:过滤出某些元素
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
//limit(3),获取过滤出的前3个元素
List<Integer> collect = integers.stream().filter(num -> num > 2).limit(3).collect(Collectors.toList());
System.out.println(collect);//[3, 4, 5]
  • skip:跳过多少个元素
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
//skip(2).limit(3),此处是先跳过2个元素再剩余流中的3个元素
List<Integer> collect = integers.stream().filter(num -> num > 2).skip(2).limit(3).collect(Collectors.toList());//[5, 6, 7]
//limit(3).skip(2),此处是获取流中的前面3个元素,再跳过两个
List<Integer> collect = integers.stream().filter(num -> num > 2).limit(3).skip(2).collect(Collectors.toList());//[5]
  • distinct:去除重复
List<Integer> integers = Arrays.asList(1, 3, 3, 3, 7);
List<Integer> collect = integers.stream().distinct().collect(Collectors.toList());
System.out.println(collect);//[1, 3, 7]

3.2映射

  • map:将元素转映射成其他形式的元素
List<Integer> integers = Arrays.asList(1, 2, 3);
List<Integer> collect = integers.stream().map(num -> num * 2).collect(Collectors.toList());//[2, 4, 6]
//此处将num映射成了String类,当然也可以映射成其他类
List<String> collect = integers.stream().map(num -> "I am "+num).collect(Collectors.toList());//[I am 1, I am 2, I am 3]
  • flatMap:将元素打平
String[] strings = {"Hello", "World"};
//[ [H,e,l,l,o] , [W,o,r,l,d] ]
List<String[]> collect = Arrays.stream(strings).map(s -> s.split("")).collect(Collectors.toList());

可以发现,我们通过map映射之后一个数组里面还有两个数组[ [H,e,l,l,o] , [W,o,r,l,d] ]这样的形式,我们可能需要的是[H, e, l, l, o, W, o, r, l, d]这样形式,那么怎么办呢,这时候就需要用到flatMap

//flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
//Arrays.stream(T[] array)
//flatMap的返回值是一个流所以这里使用Arrays.stream(),这里可以使用静态引用的方式Arrays::stream
//List<String> resultCollect = collect.stream().flatMap(Arrays::stream).collect(Collectors.toList());
List<String> resultCollect = collect.stream().flatMap(strs -> Arrays.stream(strs)).collect(Collectors.toList());
System.out.println(resultCollect);//[H, e, l, l, o, W, o, r, l, d]

3.3排序

  • sorted():增序排序
List<Integer> integers = Arrays.asList(1, 9, 3, 77, 52, 7);
List<Integer> collect = integers.stream().sorted().collect(Collectors.toList());
System.out.println(collect);
  • sorted(Comparator<? super T> comparator):自定义排序
//降序排序
List<Integer> integers = Arrays.asList(1, 9, 3, 77, 52, 7);
List<Integer> collect = integers.stream().sorted((n1, n2) -> n2 - n1).collect(Collectors.toList());
System.out.println(collect);

4.常见终止操作

4.1查找和匹配

  • allMatch:判断是否都匹配
List<Integer> integers = Arrays.asList(1, 2, 3);
//是否都大于2
boolean b = integers.stream().allMatch(num -> num > 2); // false
  • anyMatch:是否有一个匹配
List<Integer> integers = Arrays.asList(1, 2, 3);
//是否有一个大于2
boolean b = integers.stream().anyMatch(num -> num > 2); // true
  • noneMatch:判断是否都没有匹配
List<Integer> integers = Arrays.asList(1, 2, 3);
//是否都不大于5
boolean b = integers.stream().noneMatch(num -> num > 5);//true
System.out.println(b);
  • findFirst:返回第一个元素
List<Integer> integers = Arrays.asList(1, 2, 3);
Optional<Integer> first = integers.stream().findFirst();
System.out.println(first.get());
  • findAny:返回流中的任意一个

注意:串行的情况下,一般只会返回第一个结果,并行的情况下是任意一个,但是一般会固定那一个

```
System.out.println(IntStream.range(0, 100).parallel().findAny().getAsInt()); // 65
```
  • count:返回流中的数量
long count = IntStream.range(0, 100).count();//100
  • max:返回流中最大的
int max = IntStream.range(0, 100).max().getAsInt();
System.out.println(max);// 99
  • min:返回流中最小的
int min = IntStream.range(0, 100).min().getAsInt();
System.out.println(min); // 0

4.2归约

  • reduce:将上一次的结果和当前值相处理
    Optional<T> reduce(BinaryOperator<T> accumulator);
// x:第一次的值是第一个元素,之后是上一次处理完的结果,y:第一次的值是第二个元素,之后当前遍历到的值
//1. x:1,y:2
//2. x:3,y:3
//3. x:6,y:4
//4. x:10,y:5
// 求1-10的和
OptionalInt reduce = IntStream.range(1, 11).reduce((x, y) -> x + y); // 55

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

// x:第一次的值是identity,之后是上一次处理完的结果,y:当前遍历到的值
//1. x:0,y:1
//2. x:1,y:2
//3. x:3,y:3
//4. x:6,y:4
//5. x:10,y:5
// 求1-10的和
int reduce = IntStream.range(1, 11).reduce(0, (x, y) -> x + y); // 55

4.3收集

  • collect:将流转换成其他形式
    <R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);
    supplier:创建收集的容器
    accumulator:第一个参数是:最初创建的容器,第二个参数是遍历到的数据
    combiner:这个参数只有在并行的时候才会操作,作用是将两个容器合并,下面的例子种,这两个参数都是数组
ArrayList<Integer> collect = IntStream.range(1, 11).parallel().collect(ArrayList::new, (list, num) -> list.add(num), (l1, l2) -> l1.addAll(l2));
//简化写法
//ArrayList<Integer> collect = IntStream.range(1, 11).parallel().collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
System.out.println(collect); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

<R, A> R collect(Collector<? super T, A, R> collector);

List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
List<Integer> collect = integers.stream().collect(Collectors.toList());

常用的Collectors方法

  • toCollection:指定转换的集合类型
HashSet<Integer> collect = Stream.of(1, 2, 3).collect(Collectors.toCollection(HashSet::new));
System.out.println(collect); // [1, 2, 3]
  • toSet/toList:将集合转换成HashSet和ArrayList
HashSet<Integer> set = Stream.of(1, 2, 3).collect(Collectors.toSet());
List<Integer> list = Stream.of(1, 2, 3).collect(Collectors.toSet());
  • joining:可以将流中的数据拼接成字符串
// 默认直接拼接
public static Collector<CharSequence, ?, String> joining()
// 添加分隔符进行拼接
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter)
// 添加分隔符进行拼接,并且指定前后缀
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter, CharSequence prefix,  CharSequence suffix)
Stream.of("1", "2", "3").collect(Collectors.joining()); // 123
Stream.of("1", "2", "3").collect(Collectors.joining(",")); // 1,2,3
Stream.of("1", "2", "3").collect(Collectors.joining(",", "[", "]")); // [1,2,3]
  • groupingBy:对流中的数据以某个属性进行分组
    @Data
    public class Fruit {
        private String name;
        private Integer price;
    }
    
    List<Fruit> fruitList = Arrays.asList(
                    new Fruit("apple", 6), new Fruit("apple", 6),
                    new Fruit("banana", 7), new Fruit("banana", 7),
                    new Fruit("banana", 7), new Fruit("grape", 8));
    
    groupingBy(Function<? super T, ? extends K> classifier)
    底层以List的方式收集各个分组的数据
    classifier:分类的方式
    //{banana=[stream.Fruit@7b23ec81, stream.Fruit@6acbcfc0, stream.Fruit@5f184fc6], apple=[stream.Fruit@3feba861, stream.Fruit@5b480cf9], grape=[stream.Fruit@6f496d9f]}
    Map<String, List<Fruit>> collect = fruitList.stream().collect(Collectors.groupingBy(Fruit::getName));
    
    groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream)
    classifier:分类的方式
    downstream:收集各个分组的方式
    //{banana=3, apple=2, grape=1},以name去分组,并统计数量
    Map<String, Long> collect = fruitList.stream().collect(Collectors.groupingBy(Fruit::getName, Collectors.counting()));
    //{banana=[7], apple=[6], grape=[8]},以name去分组,并将价格转成set
    Map<String, Set<Integer>> collect = fruitList.stream().collect(Collectors.groupingBy(Fruit::getName, Collectors.mapping(Fruit::getPrice, Collectors.toSet())));
    
  • groupingByConcurrent:由于groupingBy是非线程安全的,而该方法则是groupingBy方法的线程安全版本,默认情况下,返回的Map类型是ConcurrentHashMap
  • toMap:将流转换成Map对象
    toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper)

注意:该方法下,生成Map的Key不能重复,不然会抛异常

```
List<Fruit> fruitList = Arrays.asList(new Fruit("apple", 6), new Fruit("banana", 7), new Fruit("grape", 8));
//{banana=7, apple=6, grape=8}
Map<String, Integer> collect = fruitList.stream().collect(Collectors.toMap(Fruit::getName, Fruit::getPrice));
```
*toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper,BinaryOperator<U> mergeFunction)*
mergeFunction:如何处理存在Key重复的问题,lambda表达式的第一个代表旧值,第二个代表新值
例如:
(o, n) -> o + n:老值+新值
(o, n) -> o :使用老值
(o, n) -> n :使用新值
```
List<Fruit> fruitList = Arrays.asList(
    new Fruit("apple", 6), new Fruit("apple", 6),
    new Fruit("banana", 7), new Fruit("banana", 7),
    new Fruit("banana", 7), new Fruit("grape", 8));
//{banana=21, apple=12, grape=8}
Map<String, Integer> collect = fruitList.stream().collect(Collectors.toMap(Fruit::getName, Fruit::getPrice, (o, n) -> o + n));
```
*toMap(Function<? super T, ? extends K> keyMapper,Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)*
```
List<Fruit> fruitList = Arrays.asList(
    new Fruit("apple", 6), new Fruit("apple", 6),
    new Fruit("banana", 7), new Fruit("banana", 7),
    new Fruit("banana", 7), new Fruit("grape", 8));
//{apple=6, banana=7, grape=8}
Map<String, Integer> collect = fruitList.stream().collect(Collectors.toMap(Fruit::getName, Fruit::getPrice, (o, n) -> n,TreeMap::new));
```
  • toConcurrentMap:toMap方法的线程安全版本
  • summarizingInt/summarizingLong/summarizingDouble方法:生成统计数据
List<Fruit> fruitList = Arrays.asList(new Fruit("apple", 6), new Fruit("banana", 7), new Fruit("grape", 8));
IntSummaryStatistics collect = fruitList.stream().collect(Collectors.summarizingInt(Fruit::getPrice));
//max:8 min:6 sum:21
System.out.println("max:"+collect.getMax()+" min:"+collect.getMin()+" sum:"+collect.getSum());
  • summingInt/summingLong/summingDouble/averagingInt/averagingLong/averagingDouble方法
    相当于上面统计出来的结果直接取值
Integer sum = fruitList.stream().collect(Collectors.averagingInt(Fruit::getPrice)); //21
  • partitioningBy方法
    该方法也是用于分组,不过是根据某一条件进行分组,最终分成满足条件的true和不满足条件的false两个分组,返回类型是Map<Boolean, Object>
Map<Boolean, List<Integer>> collect = Stream.of(1, 2, 3).collect(Collectors.partitioningBy(num -> num > 2));
System.out.println(collect);//{false=[1, 2], true=[3]}
  • reducing方法
Stream.of(1, 2, 3).collect(Collectors.reducing(0, (x, y) -> x + y));
// 等同于
Stream.of(1, 2, 3).reduce(0, (x, y) -> x + y);
  • maxBy/minBy方法
    通过比较器自定义比较方法
Optional<Integer> collect = Stream.of(1, 2, 3).collect(Collectors.maxBy((x, y) -> x - y)); // 3
//等同于
Optional<Integer> collect = Stream.of(1, 2, 3).max((x, y) -> x - y);
  • counting方法
Optional<Integer> collect = Stream.of(1, 2, 3).collect(Collectors.counting()); // 3
//等同于
Optional<Integer> collect = Stream.of(1, 2, 3).count();
  • collectingAndThen方法
    该方法接收两个参数,表示在第一个参数执行基础上,再执行第二个参数对应的函数表达式
//先取平均再乘10
Double collect = Stream.of(1, 2, 3).collect(Collectors.collectingAndThen(Collectors.averagingInt(num -> num), num -> num * 10)); // 20.0
  • mapping方法
//每个元素+1
List<Integer> collect = Stream.of(1, 2, 3).collect(Collectors.mapping(num -> num + 1, Collectors.toList())); //[2, 3, 4]

5.小测试

1.请问:

  1. 解释一下以下代码的作用
  2. 最后输出什么
List<String> strings = Arrays.asList("ABCD", "abccd", "ABcDw", "abCD", "AAA");
strings.stream()
    .map(String::toUpperCase)
    .filter("ABCDEFGH"::startsWith)
    .map(String::toLowerCase)
    .distinct()
    .forEach(System.out::println);

2.有以下的代码,问
1) 代码能否编译通过?
2) 如果能编译通过,那么运行之后会输出什么?为什么?如果不能编译通过,为什么?

List<String> list = Arrays.asList("123", "123", "456", "123");
list.stream()
        .filter("123"::equals)
        .filter(str->{
            System.out.println(str);
            return true;
        });

答案

第一题:
1)先将strings数组转换成大写,然后过滤出str -> "ABCDEFGH".startsWith(str)相匹配的字符串,接着将过滤出的数据转换成小写,然后去重,最后遍历输出每个数据
2)最后输出abcd

第二题:
1)代码可以编译通过
2)运行不会有任何输出,因为StreamAPI包括,创建Stream,中间操作,终止操作,因为这里没有终止操作,所以不会有任何输出

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343