Stream学习
Stream流式操作方便我们对集合进行操作,使用过程中有几个点需要注意,本人踩过坑。首先理解中间操作和最终操作,中间操作是惰性加载的,只有到达最终操作时,才会执行中间操作的代码。
用实例来学习stream的用法,这里有一个Boy和一个Girl类,其中Boy类有girlFriends,nickname等属性,这些下面会用到。
public class Boy {
private String name;
private List<String> nickName;
private int age;
private List<Girl> girlFriends;
public Boy(String name, List<String> nickName, int age, List<Girl> girlFriends) {
this.name = name;
this.nickName = nickName;
this.age = age;
this.girlFriends = girlFriends;
}
}
public class Girl {
private String name;
private int age;
public Girl(String name, int age) {
this.name = name;
this.age = age;
}
}
public class StreamTest {
Boy jiabaoyu;
Boy tangseng;
List<Girl> jiabaoyuGirls;
List<Girl> tangsengGirls;
@Before
public void init() {
jiabaoyuGirls = new ArrayList<>();
Girl daiyu = new Girl("林黛玉", 17);
Girl baochai = new Girl("薛宝钗", 18);
Girl xiangyun = new Girl("史湘云", 16);
jiabaoyuGirls.add(daiyu);
jiabaoyuGirls.add(baochai);
jiabaoyuGirls.add(xiangyun);
jiabaoyu = new Boy("贾宝玉", Arrays.asList("混世魔王","通灵宝玉"), 17, jiabaoyuGirls);
tangsengGirls = new ArrayList<>();
Girl zhizhjing = new Girl("蜘蛛精", 17);
Girl laoshujing = new Girl("老鼠精", 18);
Girl kongquejing = new Girl("孔雀精", 16);
tangsengGirls.add(zhizhjing);
tangsengGirls.add(laoshujing);
tangsengGirls.add(kongquejing);
tangseng = new Boy("唐僧", Arrays.asList("老和尚"), 17, tangsengGirls);
}
}
为了理解所谓的惰性,我们用forEach和stream做对比。对集合属性修改的时候,forEach能够马上得到结果,但stream在中间操作时,是不会执行具体逻辑的,只有到最终操作时,才执行。
/**
* 简单的遍历方式
*/
@Test
public void testForeach() {
jiabaoyu.getGirlFriends().forEach(girl -> girl.setAge(100));
System.out.println(jiabaoyu); // 修改成功
}
/**
* Stream是惰性加载的,当Stream在中间操作时,不执行具体逻辑,只有处于最终操作时,才执行相关代码
*/
@Test
public void testStream() {
Stream<Girl> peek = jiabaoyu.getGirlFriends().stream().peek(girl -> girl.setAge(100));
System.out.println(jiabaoyu); // 没有修改成功
Stream<Girl> distinct = peek.distinct();
System.out.println(jiabaoyu); // 没有修改成功
distinct.count();
System.out.println(jiabaoyu); // 修改成功
}
map操作返回一个集合,注意它是有返回值的,要得到最终的集合,还需要执行collect方法收集。
/**
* map操作有返回值
* 使用collect可以返回一个新的list集合
*/
@Test
public void testMap() {
jiabaoyu.setNickName(jiabaoyu.getNickName().stream().map(name -> name + "-贾宝玉").collect(Collectors.toList()));
System.out.println(jiabaoyu); // 修改成功
}
flatMap可以将嵌套的list合并为一个list,如List[List[1,2,3],List[4,5,6]] -> 1,2,3,4,5,6
/**
* 通过flatMap我们可以提取到一个list中
*/
@Test
public void testFlatMap() {
List<Boy> boys = new ArrayList<>();
boys.add(jiabaoyu);
boys.add(tangseng);
List<Girl> allGirlFriends = boys.stream().flatMap(boy -> boy.getGirlFriends().stream()).collect(Collectors.toList());
System.out.println(allGirlFriends);
}
peek操作无返回值,常用来做日志输出
/**
* peek操作无返回值
* 主要用于debug,比如可以输出流式处理中间的执行结果
*/
@Test
public void testPeek() {
jiabaoyu.setNickName(jiabaoyu.getNickName().stream().peek(name -> name = name + "-贾宝玉").collect(Collectors.toList()));
System.out.println(jiabaoyu); // 没有修改,因为peek没有返回值
jiabaoyu.getGirlFriends().stream()
.filter(girl -> girl.getAge() > 17)
.peek(girl -> System.out.println("处理后的结果" + girl))
.collect(Collectors.toList());
}
- reduce操作用于把元素组合起来得到结果
/**
* reduce把集合元素组合起来
*/
@Test
public void testReduce() {
System.out.println(Stream.of("A","B","C","D").reduce("拼接结果:", String::concat));
}
总结
介绍了几种常用的stream操作