stream用法大全

创建stream流的方式

1、通过Collection系列提供的stream()(串行) 或parallelStream()(并行)获取

List<String>list=newArrayList<>();Stream<String>stream1=list.stream();//串行流Stream<String>stream2=list.parallelStream();//并行流

2、通过Arrays中的静态方法stream() 获取数据流,接收参数为数组类型,将数组转成流

User[]u=newUser[2];Stream<User>stream3=Arrays.stream(u);

3、通过Stream类中的静态方法of()

Stream<String>stream4=Stream.of("11","2");

4、使用 Pattern.splitAsStream() 方法,将字符串分隔成流

Pattern pattern=Pattern.compile(",");Stream<String>stringStream=pattern.splitAsStream("a,b,c,d");stringStream.forEach(System.out::println);

测试数据

定义一个User 类,用于测试:

@Data@NoArgsConstructor@AllArgsConstructorpublicclassUser{privateInteger id;privateString type;privateString name;}

添加一些测试数据:

List<User>list=newArrayList<>();User user=newUser(1,"a","小明");User user1=newUser(2,"a","小红");User user2=newUser(3,"b","小李");list.add(user);list.add(user1);list.add(user2);

分组

将List里面的元素,以某个属性来分组,例如,以type分组,将type相同的放在一起

例:

//List 以type分组 Map<String,List<User>>Map<String,List<User>>groupBy=list.stream().collect(Collectors.groupingBy(User::getType));System.out.println(groupBy);

输出:

{a=[User(id=1,type=a,name=小明),User(id=2,type=a,name=小红)],b=[User(id=3,type=b,name=小李)]}

注:返回结果的map的key表示分组的属性,上面例子的分组为type,类型为String,所以Map的key属性也为String,value为集合的属性,因为这里是一个User集合的流,所以value值为list

分组个数

我们还可以求出分组个数

求分组个数很简单,我们只需要在groupingBy方法里加多一个Collectors.counting()即可

Map<String,Long>groupBy=list.stream().collect(Collectors.groupingBy(User::getType,Collectors.counting()));

输出:

{a=2,b=1}

多级分组

多级分组是先将stream流分组,然后再对分组后的数据再进行分组

例:

Map<String,Map<String,List<User>>>groupBy=list.stream().collect(Collectors.groupingBy(User::getType,Collectors.groupingBy(User::getName)));

输出:

{a={小明=[User(id=1,type=a,name=小明)],小红=[User(id=2,type=a,name=小红)]},b={小李=[User(id=3,type=b,name=小李)]}}

Stream转换List

很多时候,我们对Stream操作之后,返回的还是Stream流,很多时候,我们想返回的List或者其他类型的对象,我们就需要对Stream流进行终止操作

方法:.collect(Collectors.toList())

说明:由Stream中的值生成一个List列表,也可用collect(toSet())生成一个Set集合。

例:

String[]testStrings={"a","b","c","d"};List<String>list=Stream.of(testStrings).collect(Collectors.toList());

Stream转换Set

Stream 转换为 Set,Set不允许重复值,没有顺序,使用collect(Collectors.toSet())即可

例:

Set<User>collect=list.stream().collect(toSet());

Stream转换Map

Stream 转换为 Map,使用.collect(Collectors.toMap())即可。

Collectors.toMap 有三个重载方法:

toMap(Function<?superT,?extendsK>keyMapper,Function<?superT,?extendsU>valueMapper);toMap(Function<?superT,?extendsK>keyMapper,Function<?superT,?extendsU>valueMapper,BinaryOperator<U>mergeFunction);toMap(Function<?superT,?extendsK>keyMapper,Function<?superT,?extendsU>valueMapper,BinaryOperator<U>mergeFunction,Supplier<M>mapSupplier);

参数含义分别是:

keyMapper:Key 的映射函数

valueMapper:Value 的映射函数

mergeFunction:当 Key 冲突时,调用的合并方法

mapSupplier:Map 构造器,在需要返回特定的 Map 时使用

三种重载方法,所以就有三种不同的用法

1、只指定key和value的映射

例:

//以ID为key,User对象为value组成一个mapMap<Integer,User>collect1=list.stream().collect(toMap(User::getId,userObject->userObject));collect1.entrySet().stream().forEach(System.out::println);

输出:

1=User(id=1,type=a,name=小明)2=User(id=2,type=a,name=小红)3=User(id=3,type=b,name=小李)

当然,你也可以指定某个属性为value值

例:

//以ID为key,Name为valueMap<Integer,String>collect1=list.stream().collect(toMap(User::getId,User::getName));

2、使用第二个重载方法,需要传多一个参数,即需要传入合并函数(用于当发生key冲突时,如何处理,即插入一个key已经存在的数据时的处理)

例:传入(o,n)->o合并函数

Map<String,String>collect1=list.stream().collect(toMap(User::getType,User::getName,(o,n)->o));//遍历mapcollect1.entrySet().stream().forEach(System.out::println);

(o,n)->o:其中o和n表示旧的value和新的value,只是一个名称,可以随意换成其他的名称,->o表示保留旧的value值,不插入新的value值

如我们运行上面的例子,输出如下

a=小明b=小李

我们的数据中type值相同的有两个,因为小明是先插入的,所以合并函数(o,n)->o保留的是先插入的数据

如果我们想用后插入的数据来替换旧的数据,那么只需要把返回变量变一下即可,如下

(o,n)->n

当然,我们还可以返回其他形式的value,比如拼接两个value等等操作

例:

(o,n)->o+n

3、调用第三个重载方法,需要传4个参数,即在合并函数之后,再传入一个函数,用于自定义返回 Map的 类型

我们看一下三个参数的tomap源码

publicstatic<T,K,U>Collector<T,?,Map<K,U>>toMap(Function<?superT,?extendsK>keyMapper,Function<?superT,?extendsU>valueMapper,BinaryOperator<U>mergeFunction){returntoMap(keyMapper,valueMapper,mergeFunction,HashMap::new);}

可以看到,其实就是使用四个参数的重载方法,只不过替我们默认定义了返回的map类型为HashMap

有时候,我们不想返回HashMap,我们想返回ConcurrentHashMap,很简单,如下

list.stream().collect(toMap(User::getType,User::getName,(o,n)->o,ConcurrentHashMap::new));

如果想返回TreeMap,那么将ConcurrentHashMap::new替换成TreeMap::new即可

自定义实现Collection的数据结构收集

除了使用Collectors提供了toMap,toList等方法之外,我们还可以自定义Collection的数据结构收集

例:

//用LinkedList收集list.stream().collect(Collectors.toCollection(LinkedList::new));//用TreeSet收集list.stream().collect(Collectors.toCollection(TreeSet::new));

stream字符串拼接

stream字符串拼接使用Collectors.joining()方法就可将字符串拼接在一起,而且joining方法可以传入一个字符参数,这样就可以在拼接的每个元素中间都拼接上该字符

注:joining方法只对String类型的流有效

joining还有一个三个传参的重载方法,

publicstaticCollector<CharSequence,?,String>joining(CharSequence delimiter,CharSequence prefix,CharSequence suffix)

参数说明:

delimiter:元素间隔字符

prefix:前缀,即拼接字符开头加上的字符

suffix:后缀,即拼接字符结尾加上的字符

例:

List<String>stringList=Arrays.asList("a","b","c");System.out.println(stringList.stream().collect(joining()));System.out.println(stringList.stream().collect(joining("xxx")));

输出:

abc

axxxbxxxc

排序

stream流中的排序方法为sorted()

sorted(),产生一个新流,其中按自然顺序排序。

sorted(Comparator),产生一个新流,其中按Comparator比较器顺序排序。

注:如果是自定义的对象使用sorted()方法,需要实现Comparable 接口,基本类型可以直接使用。

Comparable接口只有一个compareTo方法publicinterfaceComparable<T>{intcompareTo(T t);}

int compareTo(T t)方法说明

定义:比较此对象与指定对象的顺序。

返回:负整数、零或正整数。如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。

例:

//按ID降序@OverridepublicintcompareTo(User user){returnuser.id-this.id;}

升序

this.id-user.id

如果我们不想在对象里实现Comparable 接口,那么可以使用sorted(Comparator)方法,传入参数为Comparator比较器

例:根据type进行排序,reversed()为倒序

list.stream().sorted(Comparator.comparing(User::getType).reversed()).collect(toList());

注:comparing方法是Comparator内部实现的一个方法,我们可以传入某个属性,然后会替我们生成一个该属性的升序Comparator比较器

总数

求一个stream的长度,使用.collect(Collectors.counting()即可

list.stream().collect(Collectors.counting())

循环遍历

循环遍历使用forEach方法即可

//循环遍历输出每个元素collect.stream().forEach(System.out::println);

取最大值

stream流有一个max方法可以取出stream流中符合要求的最大值

max方法源码如下:

Optional<T>max(Comparator<?superT>var1);

传入的参数为Comparator比较器,返回的是一个Optional对象,使用Optional的get方法,即可取出封装的对象

如果是基本类,则可以使用基本类型的compare方法

例:

//使用Comparator的comparing方法做参数User user3=list.stream().max(Comparator.comparing(User::getId)).get();//取出User集合中最大的IDInteger max=list.stream().map(x->x.getId()).max(Integer::compare).get();

取最小值

stream流有一个min方法可以取出stream流中符合要求的最小值

用法和max方法一样

例:

//取出id最小的userUser min=list.stream().min((u1,u2)->u1.getId().compareTo(u2.getId())).get();

注:上面使用的是lambda表达式的Comparator比较器

取平均值

取平均值有averagingInt,averagingLong,averagingDouble三种方法,只是类型不一样,返回的都是Double

averagingInt源码如下

publicstatic<T>Collector<T,?,Double>averagingInt(ToIntFunction<?superT>mapper)

例:

//取出id的平均值list.stream().collect(Collectors.averagingInt(User::getId));

筛选

stream流的筛选方法为filter(predicate)

filter(predicate)-接收lambda,从流中排除某些元素,保留符合条件的元素

该方法接受一个谓词predicate方法(返回Boolean的函数)作为参数

例:

//筛选出id大于1的userList<User>collect2=list.stream().filter(x->x.getId()>1).collect(toList());

输出

User(id=2,type=a,name=小红)User(id=3,type=b,name=小李)

注意:stream流的filter方法是保留符合条件的元素

去重

stream流的筛选方法为distinct(),它会根据元素的hashcode()和equals()去除重复的元素

例:

List<String>stringList=Arrays.asList("a","b","c","a");stringList.stream().distinct().forEach(System.out::println);

输出:

a

b

c

可以看到重复的元素a只剩下了一个

截断流limit

limit(n)-截断流,使其元素不超过给定数量

源码如下

Stream<T>limit(longvar1);

传入的是一个Long类型的整数

例:

stringList.stream().limit(2L);

使用了这个limit方法后,stream流就会只剩下2个元素

映射

Stream中包含5个映射方法:map、mapToDouble、mapToInt、mapToLong和flatMap。

map,接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每一个元素上,并将其映射成一个新的元素。

map最常用的操作就是取出元素中的某个属性,形成一个新的流

例:

//将Uer集合中的Name属性全部取出来形成一个新的流,再转换成集合List<String>nameList=list.stream().map(User::getName).collect(toList());

mapToDouble/mapToInt/mapToLong,接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新DoubleStream/IntStream/LongStream。

匹配

anyMatch是否有一个元素匹配条件,返回的是一个boolean类型元素

源码如下:

booleananyMatch(Predicate<?superT>var1);

例:

//判断是否有元素的type等于bbooleananyMatch=list.stream().anyMatch(x->x.getType().equals("b"));

allMatch,检查是否匹配所有元素,需要stream流的所有元素都匹配条件才返回true

noneMatch,检查是否没有匹配所有元素。

//判断list对象内指定字段

userInfos.stream().anyMatch(userInfo1 ->userInfo1.getId().equals(userInfo.getId()));

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

推荐阅读更多精彩内容