实际中的Stream流的用法
不同对象怎么生成stream流对象
package stream;/*** @author 刘诗良* @version 1.0* @Description*/
import java.util.*;
import java.util.stream.Stream;public class StreamDemo {public static void main(String[] args) {//Collection体系的集合可以使用默认方法stream()生成流List<String> list = new ArrayList<>();Stream<String> listStream = list.stream();Set<String> set = new HashSet<>();Stream<String> setStream = set.stream();//Map体系的集合间接生成流Map<String,Integer> map = new HashMap<>();Stream<String> keyStream = map.keySet().stream();Stream<Integer> valueStream = map.values().stream();Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();//数组可以通过Stream接口的静态方法of(T...values)生成流String[] strArray = {"hello","world","java"};Stream<String> strArrayStream = Stream.of(strArray);Stream<String> strArrayStream2 = Stream.of("hello", "world", "java");Stream<Integer> intStream = Stream.of(10, 20, 30);}
}
除了上面明显的方法调用对象是null外,不会出现异常。
forEach和map的不同
forEach会改变原集合(不管是否是引用)
package yimeng;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.*;public class StreamTest {public static void main(String[] args) {List<Person> userList = new ArrayList<>();userList.add(new Person("段誉",25));userList.add(new Person("萧峰",40));userList.add(new Person("虚竹",30));userList.add(new Person("无涯子",100));userList.add(new Person("慕容复",35));userList.add(new Person("云中鹤",45));System.out.println("-------原集合--------");System.out.println(userList);System.out.println("-------修改引用类型--------");userList.stream().forEach(u -> {u.setName("天龙八部-" + u.getName());});System.out.println(userList);System.out.println("-------修改非引用类型--------");userList.stream().forEach(u -> {u.setAge(12);});System.out.println(userList);}
}@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class Person {private String name;private int age;
}
不管修改引用类型还是非引用类型的数据,都会影响原来集合。
除了上面明显的方法调用对象是null外,不会出现异常。forEach方法中u.setName(“天龙八部-” + u.getName());、u.setAge(12);的u为null会出现异常。
map不会改变原集合(不管是否是引用)
package yimeng;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.*;public class StreamTest {public static void main(String[] args) {List<Person> userList = new ArrayList<>();userList.add(new Person("段誉",25));userList.add(new Person("萧峰",40));userList.add(new Person("虚竹",30));userList.add(new Person("无涯子",100));userList.add(new Person("慕容复",35));userList.add(new Person("云中鹤",45));System.out.println("-------原集合--------");System.out.println(userList);System.out.println("-------修改引用类型--------");userList.stream().map(u -> {u.setName("天龙八部-" + u.getName());return 1234;});System.out.println(userList);System.out.println("-------修改非引用类型--------");userList.stream().map(u -> {u.setAge(12);return "测试";});System.out.println(userList);}
}@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class Person {private String name;private int age;
}
除了上面明显的方法调用对象是null外,不会出现异常。forEach方法中u.setName(“天龙八部-” + u.getName());、u.setAge(12);的u为null会出现异常。
map必须要有返回值,但是forEach不用
看到下面这个map这样写是错的。
map方法返回的还是一个流,map参数的方法要有返回值,forEach没有返回值,且是终结流,且它的参数的方法没有返回值
注意:
下面这种,你把变量指向一个新的引用对象,是不会改变原来集合的。因为,下面的forEach相当于是把流中指向userList正在遍历的元素的引用赋值给了forEach中定义的变量,然后去执行操作的。所以上面的forEach案例你操作u.setAge()能改变原来集合,其实就是因为你u指向了真正的对象,所以u.setAge()就是让真正的对象去改变值,所以能改变原来集合。至于下面这种为什么不行,就是因为原来u是指向正在遍历的对象的,但是你u=new Person(“1”,1);就是把u指向另一个对象,所以执行就不会改变原来集合中的元素。
package yimeng;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.*;
import java.util.stream.Collectors;public class StreamTest {public static void main(String[] args) {List<Person> userList = new ArrayList<>();userList.add(new Person("段誉",25));userList.add(new Person("萧峰",40));userList.add(new Person("虚竹",30));userList.add(new Person("无涯子",100));userList.add(new Person("慕容复",35));userList.add(new Person("云中鹤",45));userList.stream().forEach(u -> {u=new Person("1",1);u.setAge(111);});System.out.println(userList);}
}@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class Person {private String name;private int age;
}
去null
package yimeng;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;public class StreamTest {public static void main(String[] args) {List<UserInfo> list=new ArrayList<>();list.add(UserInfo.builder().userId(1L).userName("张三").build());list.add(UserInfo.builder().userId(2L).amount(10).userName("李四").build());list.add(UserInfo.builder().userId(3L).amount(15).build());List<String> userNameList = list.stream().map(UserInfo::getUserName).filter(Objects::nonNull).collect(Collectors.toList());System.out.println(userNameList);}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class UserInfo {private Long userId;private String userName;private String status;private Integer amount;private Date createDate;
}
但是这种方式不能去空字符串。
比如:
解决:
注意:上面的map方法调用前流中的元素如果是null,那么执行UserInfo::getUserName会出现空指针异常。
解决:
处理后返回一个新的List
package yimeng;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.ObjectUtils;import java.util.*;
import java.util.stream.Collectors;public class StreamTest {public static void main(String[] args) {List<UserInfo> list=new ArrayList<>();list.add(UserInfo.builder().userId(1L).userName("张三").build());list.add(UserInfo.builder().userId(2L).amount(10).userName("李四").build());list.add(UserInfo.builder().userId(3L).amount(15).build());// List<String>转List<Long>List<Long> toLong = Arrays.asList("1","2").stream().map(Long::parseLong).collect(Collectors.toList());System.out.println(toLong);// List<Bean>转类型List<String>,其中新list的元素为老list中每一个元素的用户名。List<String> userNameList = list.stream().map(UserInfo::getUserName).collect(Collectors.toList());System.out.println(userNameList);}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class UserInfo {private Long userId;private String userName;private String status;private Integer amount;private Date createDate;
}
返回一个新的List,要求没有null
package yimeng;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.ObjectUtils;import java.util.*;
import java.util.stream.Collectors;public class StreamTest {public static void main(String[] args) {List<UserInfo> list=new ArrayList<>();list.add(UserInfo.builder().userId(1L).userName("张三").build());list.add(UserInfo.builder().userId(2L).amount(10).userName("李四").build());list.add(UserInfo.builder().userId(3L).userName("王五").build());list.add(UserInfo.builder().userId(4L).amount(15).build());list.add(UserInfo.builder().userId(5L).amount(25).userName("").build());list.add(UserInfo.builder().userId(6L).amount(45).userName(" ").build());// List<Bean>转类型List<String>,其中新list的元素为老list中每一个元素的用户名。排除空。List<Integer> amountList1 = list.stream().map(i -> i.getAmount() == null ? 0 : i.getAmount()).collect(Collectors.toList());System.out.println(amountList1);}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class UserInfo {private Long userId;private String userName;private String status;private Integer amount;private Date createDate;
}
去除重复
distinct()方法的判断重复和HashMap判断重复的做法是一样的。是通过hashCode()方法和equals()方法来判断是否重复的(java中有约定,重写equals()方法就得重写hashCode(),并且约定重写的规则是hashCode()判定相同,equals()方法可以判定为不同。equals判定为相同,hashCode()得要判定为相同,当然你不遵循约定也是OK的)。distinct()判定如果重复了,那么只会留下先遍历到的那个元素,后面遍历到的被判定为重复的元素,而被舍弃。判断重复的规则是:通过“正在遍历的元素”.equals(“加入到新集合中的每一个元素”)来判断是否重复,只要存在返回true,那么就判定为重复,判断为重复那么正在遍历的元素就不会被加入到新集合。如果都不返回true,就判断为不重复,那么就会把正在遍历的元素加入到新集合中去
测试只有equals()判断重复,是不是被认定为重复:
package yimeng;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.*;
import java.util.stream.Collectors;public class StreamTest {public static void main(String[] args) {List<UserInfo> list=new ArrayList<>();list.add(UserInfo.builder().userId(1L).userName("张三").build());list.add(UserInfo.builder().userId(2L).amount(10).userName("李四").build());list.add(UserInfo.builder().userId(3L).userName("王五").build());list.add(UserInfo.builder().userId(4L).amount(15).build());list.add(UserInfo.builder().userId(5L).amount(15).userName("").build());list.add(UserInfo.builder().userId(6L).amount(45).userName(" ").build());// 没有去重复List<UserInfo> amountList = list.stream().collect(Collectors.toList());System.out.println("没有去重:"+amountList);// 去重复List<UserInfo> amountList1 = list.stream().distinct().collect(Collectors.toList());System.out.println("去重:"+amountList1);}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class UserInfo {private Long userId;private String userName;private String status;private Integer amount;private Date createDate;@Overridepublic boolean equals(Object o) {// 一定返回truereturn true;}@Overridepublic int hashCode() {Random random = new Random();int randomNumber = random.nextInt();System.out.println(randomNumber);// 不会重复return randomNumber;}
}
结果(没有被判断为重复):
两个方法都判断为重复,才会被认定为重复。并且只会留下第一个元素,后面被判断为重复的元素不会被留下:
package yimeng;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.*;
import java.util.stream.Collectors;public class StreamTest {public static void main(String[] args) {List<UserInfo> list=new ArrayList<>();list.add(UserInfo.builder().userId(1L).userName("张三").build());list.add(UserInfo.builder().userId(2L).amount(10).userName("李四").build());list.add(UserInfo.builder().userId(3L).userName("王五").build());list.add(UserInfo.builder().userId(4L).amount(15).build());list.add(UserInfo.builder().userId(5L).amount(15).userName("").build());list.add(UserInfo.builder().userId(6L).amount(45).userName(" ").build());// 没有去重复List<UserInfo> amountList = list.stream().collect(Collectors.toList());System.out.println("没有去重:"+amountList);// 去重复List<UserInfo> amountList1 = list.stream().distinct().collect(Collectors.toList());System.out.println("去重:"+amountList1);}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class UserInfo {private Long userId;private String userName;private String status;private Integer amount;private Date createDate;@Overridepublic boolean equals(Object o) {// 一定返回truereturn true;}@Overridepublic int hashCode() {// 重复return 10;}
}
判断重复的规则(通过“正在遍历的元素”.equals(“加入到新集合中的每一个元素”)来判断是否重复,只要存在返回true,那么就判定为重复,判断为重复那么正在遍历的元素就不会被加入到新集合。如果都不返回true,就判断为不重复,那么就会把正在遍历的元素加入到新集合中去):
package yimeng;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.*;
import java.util.stream.Collectors;public class StreamTest {public static void main(String[] args) {List<UserInfo> list=new ArrayList<>();list.add(UserInfo.builder().userId(1L).userName("张三").build());list.add(UserInfo.builder().userId(2L).amount(10).userName("李四").build());list.add(UserInfo.builder().userId(3L).userName("王五").build());list.add(UserInfo.builder().userId(2L).amount(15).build());list.add(UserInfo.builder().userId(3L).amount(15).userName("").build());list.add(UserInfo.builder().userId(6L).amount(45).userName(" ").build());// 没有去重复List<UserInfo> amountList = list.stream().collect(Collectors.toList());System.out.println("没有去重:"+amountList);// 去重复List<UserInfo> amountList1 = list.stream().distinct().collect(Collectors.toList());System.out.println("去重:"+amountList1);}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class UserInfo {private Long userId;private String userName;private String status;private Integer amount;private Date createDate;@Overridepublic boolean equals(Object o) {// “正在遍历的元素”.equals(“每一个元素”)来判断是否重复,只要有一个返回true就表示存在重复。所以,运行结果是除了userId为1的那个张三外,都会被判定为存在重复,又因为重复的话,后面的元素会被舍弃,那么结果就只有userId为1的元素。因为上面所有元素的userId中,没有userId比1小的,所以只有“张三”那个元素对比每一个元素时,“this.userId>((UserInfo)o).getUserId()”都为false。return this.userId>((UserInfo)o).getUserId();}@Overridepublic int hashCode() {// 重复return 10;}
}
package yimeng;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.*;
import java.util.stream.Collectors;public class StreamTest {public static void main(String[] args) {List<UserInfo> list=new ArrayList<>();list.add(UserInfo.builder().userId(2L).amount(10).userName("李四").build());list.add(UserInfo.builder().userId(3L).userName("王五").build());list.add(UserInfo.builder().userId(2L).amount(15).build());list.add(UserInfo.builder().userId(1L).userName("张三").build());list.add(UserInfo.builder().userId(3L).amount(15).userName("").build());list.add(UserInfo.builder().userId(6L).amount(45).userName(" ").build());// 没有去重复List<UserInfo> amountList = list.stream().collect(Collectors.toList());System.out.println("没有去重:"+amountList);// 去重复List<UserInfo> amountList1 = list.stream().distinct().collect(Collectors.toList());System.out.println("去重:"+amountList1);}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class UserInfo {private Long userId;private String userName;private String status;private Integer amount;private Date createDate;@Overridepublic boolean equals(Object o) {// “正在遍历的元素”.equals(“每一个元素”)来判断是否重复,只要有一个返回true就表示存在重复。所以,运行结果是除了userId为1的那个张三外,都会被判定为存在重复,又因为重复的话,后面的元素会被舍弃,那么结果就只有userId为1的元素。因为上面所有元素的userId中,没有userId比1小的,所以只有“张三”那个元素对比每一个元素时,“this.userId>((UserInfo)o).getUserId()”都为false。return this.userId>((UserInfo)o).getUserId();}@Overridepublic int hashCode() {// 重复return 10;}
}
转为Map
package yimeng;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.*;
import java.util.stream.Collectors;public class StreamTest {public static void main(String[] args) {List<UserInfo> list=new ArrayList<>();list.add(UserInfo.builder().userId(1L).userName("张三").build());list.add(UserInfo.builder().userId(2L).amount(10).userName("李四").build());list.add(UserInfo.builder().userId(3L).userName("王五").build());list.add(UserInfo.builder().userId(4L).amount(15).build());list.add(UserInfo.builder().userId(5L).amount(15).userName("").build());list.add(UserInfo.builder().userId(6L).amount(45).userName(" ").build());Map<Long, UserInfo> map1 = list.stream().collect(Collectors.toMap(UserInfo::getUserId, i -> i));System.out.println(map1);}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class UserInfo {private Long userId;private String userName;private String status;private Integer amount;private Date createDate;
}
但是,如果键有重复,那么会出现异常。
解决:
package yimeng;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.*;
import java.util.stream.Collectors;public class StreamTest {public static void main(String[] args) {List<UserInfo> list=new ArrayList<>();list.add(UserInfo.builder().userId(1L).userName("张三").build());list.add(UserInfo.builder().userId(2L).amount(10).userName("李四").build());list.add(UserInfo.builder().userId(3L).userName("王五").build());list.add(UserInfo.builder().userId(3L).amount(15).build());list.add(UserInfo.builder().userId(5L).amount(15).userName("1").build());list.add(UserInfo.builder().userId(6L).amount(45).userName("jdlf").build());Map<Long, String> map = list.stream().collect(Collectors.toMap(UserInfo::getUserId, i -> i.getUserName() == null ? "null" : i.getUserName(), (k1, k2) -> k2));System.out.println(map);}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class UserInfo {private Long userId;private String userName;private String status;private Integer amount;private Date createDate;
}
取符合条件的一个元素
使用limit
package yimeng;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.*;
import java.util.stream.Collectors;public class StreamTest {public static void main(String[] args) {List<UserInfo> list=new ArrayList<>();list.add(UserInfo.builder().userId(1L).userName("张三").build());list.add(UserInfo.builder().userId(2L).amount(10).userName("李四").build());list.add(UserInfo.builder().userId(3L).userName("王五").build());list.add(UserInfo.builder().userId(3L).amount(15).build());list.add(UserInfo.builder().userId(5L).amount(15).userName("1").build());list.add(UserInfo.builder().userId(6L).amount(45).userName("张三").build());List<UserInfo> collect = list.stream().filter(u -> "张三".equals(u.getUserName())).limit(1).collect(Collectors.toList());System.out.println(collect.get(0));List<UserInfo> collect1 = list.stream().filter(u -> "张三111".equals(u.getUserName())).limit(1).collect(Collectors.toList());System.out.println(collect1.get(0));}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class UserInfo {private Long userId;private String userName;private String status;private Integer amount;private Date createDate;
}
使用findAny或者findFirst(找符合条件的元素可以用这个两个方法)
并行流时findAny的效率会比findFirst高一些,串行流的话两者效率一样,并且在并行流的情景下,我们一般是不会有需求知道哪一个元素是第一个被放到新集合去的,因为都用并行流了,说明不在意结果的集合中的元素的顺序了,所以并行流里一般也不会使用findFirst方法,所以,建议使用findAny。
package yimeng;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.*;
import java.util.stream.Collectors;public class StreamTest {public static void main(String[] args) {List<UserInfo> list=new ArrayList<>();list.add(UserInfo.builder().userId(1L).userName("张三").build());list.add(UserInfo.builder().userId(2L).amount(10).userName("李四").build());list.add(UserInfo.builder().userId(3L).userName("王五").build());list.add(UserInfo.builder().userId(3L).amount(15).build());list.add(UserInfo.builder().userId(5L).amount(15).userName("1").build());list.add(UserInfo.builder().userId(6L).amount(45).