Java8出了一个Stream流式编程,在开发中或多或少用到接触过。怎么说呢!举个例子把,一起我们在遍历一个集合的时候,我们是从外部去遍历的,然后才能拿到结果,这样来效率就会变得相对低一点。而这个时候我们去内部去遍历集合的时候,直接从内部拿数据。减少资源消耗,提升效率。

一、什么是Stream呢?

Stream它并不是一个容器,它只是对容器的功能进行了增强,添加了很多便利的操作,例如查找、过滤、分组、排序等一系列的操作。并且有串行、并行两种执行模式,并行模式充分的利用了多核处理器的优势,使用fork/join框架进行了任务拆分,同时提高了执行速度。简而言之,Stream就是提供了一种高效且易于使用的处理数据的方式。

二、一个普通执行流程

java Stream并行流线程安全 stream流并行处理_List


从这简单的图可以看出,总共就只有三步,相对来说还是比较容易接受。第一步是创建Stream这个容器,然后再从这个集合或者数组中去获取这个流。第二步则是一些中间操作,比如对数据进行处理啊。第三步则就是收集我们处理的数据。

public class Stream {
    public static void main(String[] args) {
        list();//传统for遍历
        bigForList();//增强for遍历
        iteratorList();//使用迭代器iterator遍历
     }

    private static void list(){
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        int i;
        int size;
        for (i=0,size=list.size(); i<size; i++){
            Integer integer = list.get(i);
            System.out.println(integer);
        }
    }
    private static void bigForList(){
        List<String > arrlist = new ArrayList<>();
        arrlist.add("张三");
        arrlist.add("李四");
        arrlist.add("王二");
        for (String list :arrlist){
            System.out.println(list);
        }
    }
    private static void iteratorList(){
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("demo");
        list.add("test");
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()){
            String s = iterator.next();
            System.out.println(s);
        }
    }

以上就是我们我们之前常用的三种遍历方式,可能大家更加倾向于这三种,因为我们在平时开发中或者自己练习的时候。用的比较多,也就慢慢就接受了。

1.1 Steam方式遍历

但是如果数据量一大?大量数据进行遍历的时候那个这效率,就变得低起来了。所以,Stream就出来了。首先我们看看code:

//look code
public static void main (String[] args){
        //List<String> sList = Arrays.asList("zhangsan","heqing","lisi","...");创建list方式1
        List<String> list = new ArrayList<String>();//创建list方式2
		    list.add("1");
		    list.add("2");
		    list.add("3");
		    /*
		    Iterator<String> iterator = list.iterator();
		    while (iterator.hasNext()) {
		    	System.out.println(iterator.next());
			}
			*/
		    Stream<String> stream = list.stream();
		    stream.forEach(System.out::println);
		    
			List<String> list2 = new ArrayList();
			list.stream().forEach(str ->{
	    		list2.add(str);
			});
			System.out.println(list2);
		    /*
		    Stream<String> streams = list.parallelStream();
		    streams.forEach(System.out::println);
		    */

Stream是顺序流,由主线程按顺序对流执行操作

parallelStream是并行流,内部以多线程并行执行的方式对流进行操作,需要注意使用并行流的前提是流中的数据处理没有顺序要求(会乱序,即使用了forEachOrdered)。

Stream结果

java Stream并行流线程安全 stream流并行处理_List_02


parallelStream结果

java Stream并行流线程安全 stream流并行处理_stream_03

1.2 创建顺序流

//look code
public static void main (String[] args){
        List<Integer> sList = Arrays.asList(1,2,3,4,5);
        //把顺序流通过.parallel()转化为并行流
        Optional<Integer> findFirst = sList.stream().parallel().filter(x->x>5).findFirst();
          //创建顺序流
        java.util.stream.Stream<String> stream = sList.stream();
        //创建并接流
        java.util.stream.Stream<String> stringStream = sList.parallelStream();
        stream.forEach(System.out::println);
    }

在Stream中提供了很多方法比如filter
在以前我们要去一个集合或者数组中筛选我们想要的数据。还要进行一系列的操作,首先创建数组或者集合,然后遍历 然后判断,然后写逻辑等等。。。很麻烦

public static void main(String [] args){
	List list= new ArrayList<>();
	list.add();
	....
	for(){
	....
	....
	...
	逻辑代码等。。。。
	}		
}

1.3 判断条件

但是如果用到了Stream后就没不会有这么判断条件。切代码量小了,效率高了。谁又不想少写代码呢?早点下班不香吗?
废话不多说,look code;

public static void main(String[] args){
        List<Integer> list = Arrays.asList(1,2,3,4,5,52,46,48,0,12);
        java.util.stream.Stream<Integer> stream= list.stream();
        //通过filter过滤去,获取list中大于12的数据
        stream.filter(x -> x > 12).forEach(System.out::println);
    }
    //就三行代码完成,如果按照以前的写法,起码10行把!

还有映射 map、flatMap等
look code
map:一个元素类型为 T 的流转换成元素类型为 R 的流,这个方法传入一个Function的函数式接口,接收一个泛型T,返回泛型R,map函数的定义,返回的流,表示的泛型是R对象;

//<R> Stream<R> map(Function<? super T, ? extends R> mapper);

 public static void main(String [] args) {
    Stream.of("张三:20").map(s -> {
        String[] str = s.split(":");
        Person person = new Person(str[0],Integer.valueOf(str[1]));
        return person;
    }).forEach(Person -> System.out.println(Person));
    }
@Test
    public void deleteAssetByOrderNum() {
    	 List<String> orderNums = new ArrayList<>();
    	 orderNums.add("PD0001620210804005");
    	 orderNums.add("PD0001620210804004");
    	 orderNums.add("PD0001620210804003");
    	 List<String> orders = orderNums.stream().map(e -> "'"+e+"'").collect(Collectors.toList());
    	 System.out.println(orders);
         String ordersJoin = String.join(",", orders);
         System.out.println(orderNums);
    }

将结果前后加上单引号,然后利用String.join将数组转换成String字符串

java Stream并行流线程安全 stream流并行处理_java Stream并行流线程安全_04

@Test
	public void test() {
		String sql = "select wfcode from  aac_myfocus_tab  where  adnum =  '60052760'";
		List<Map<String, Object>> list = gdao.executeJDBCSqlQuery(sql);
		System.out.println("原始数据结构:"+list.toString());
		List listWfcode = new ArrayList();
		List list2 = new ArrayList();
		for (int i = 0; i < list.size(); i++) {
			listWfcode.add(((Map<String, Object>) list.get(i)).get("WFCODE"));
		}
		System.out.println("解析方式1:"+listWfcode.toString());
			
		String collect = list.stream().filter(map1 -> null != map1.get("WFCODE"))
		        .map(str -> str.get("WFCODE").toString())
		        .collect(Collectors.joining(","));
		System.out.println("解析方式2:"+collect);
		
		List<String> collect1 = list.stream().filter(map1 -> null != map1.get("WFCODE"))
		        .map(str ->"'"+ str.get("WFCODE").toString()+"'")
		        .collect(Collectors.toList());
		System.out.println("解析方式3:"+collect1);
			
	}

java Stream并行流线程安全 stream流并行处理_java Stream并行流线程安全_05


将String字符串转换成数组

String str= "PD0000720210524001,PD0000720210522006"; 
    	String[] array = str.split(",");
    	System.out.println(array);
    	StringBuffer sb = new StringBuffer();
    	for(int i=0;i<array.length;i++) {
    		//System.out.println(array[i]);
    		sb.append("'"+array[i]+"',");
    	}
    	
    	System.out.println(sb.toString());
    	System.out.println(Arrays.asList(array));
    	
    	List<String> orders = Arrays.asList(array).stream().map(e -> "'"+e+"'").collect(Collectors.toList());
    	String ordersJoin = String.join(",", orders);
    	System.out.println(ordersJoin);

java Stream并行流线程安全 stream流并行处理_stream_06


flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

look code

public static void main(String [] args) {
        List<String> list = Arrays.asList("k,l,s,x,z","1,5,2,4,8");
        List<String> newList = list.stream().flatMap(s -> {
            String[] str = s.split(",");
            Stream<String> stream = Arrays.stream(str);
            return stream;
        }).collect(Collectors.toList());
        System.out.println("处理前的集合"+list);
        System.out.println("处理后的集合"+newList);
    }
List<LinkedHashMap<String, Object>> listData = new ArrayList<>();
        //举措数据data1
        List<LinkedHashMap<String, Object>> initiativeData = getInitiativeData(year);
        //准备对举措数据去重data1
        List<LinkedHashMap<String, Object>> initiativeDistinctData = initiativeData;
        //项目成员data2
        List<LinkedHashMap<String, Object>> projectMembers = getProjectMembers(year);

        //举措数据data1,根据customer_id去重
        initiativeDistinctData = distinctCommonList(initiativeDistinctData,"customer_id");

        for (int i = 0; i < initiativeDistinctData.size(); i++) {
            String id = initiativeDistinctData.get(i).get("customer_id").toString();
            //获取id对应data1,得到data4
            List<LinkedHashMap<String, Object>> data4 = initiativeData.stream().filter(v -> v.get("customer_id").equals(id)).collect(Collectors.toList());
            //根据data3中的id,项目成员data2
            List<LinkedHashMap<String, Object>> members = projectMembers.stream().filter(v -> v.get("customer_id").equals(id)).collect(Collectors.toList());
            //备用邮件接收人
            List<LinkedHashMap<String, Object>> customers = getBackupCustomers(id);

            if (members.size() != 0) {
                //去重members中的userid,得到data5
                members = distinctCommonList(members,"userid");
            } else {
                //去重customers中的username,得到data5
                customers = distinctCommonList(customers,"username");
            }
}

1.4 对数据集进行数据计算

List<CmsHistoryPushTaskResp> cmsUserPushList = cmsPushTaskMapper.pageList(req,paging);

        List<CmsHistoryPushTaskResp> cmsUserPushListTemp = new ArrayList<>();

        cmsUserPushList.stream().parallel().forEach(x -> {
            CmsHistoryPushTaskResp cmsTask = new CmsHistoryPushTaskResp();
            cmsTask.setPushTarget(x.getPushTarget());
            if(x.getNumberOfDelivered() != null && x.getNumberOfPushed() != null &&  x.getNumberOfPushed()!= 0){
                //保留2位小数
                DecimalFormat df = new DecimalFormat("0.00");
                String s = df.format((float)x.getNumberOfDelivered()/x.getNumberOfPushed());
                cmsTask.setDeliveryRate(s);
            }
            cmsTask.setTemplateName(x.getTemplateName());
            cmsTask.setPushStatus(x.getPushStatus());
            cmsTask.setPushMode(x.getPushMode());
            cmsTask.setPushTaskName(x.getPushTaskName());
            cmsTask.setPushTime(x.getPushTime());
            cmsTask.setPushType(x.getPushType());
            cmsTask.setTemplateId(x.getTemplateId());
            cmsUserPushListTemp.add(cmsTask);
        });

1.5 两个集合的交集、差集、去重

例如:找出两个班 名字相同的学生

public class Student {
 
    private String studentNo;
    //名字
    private String studentName;
 
    public Student(String studentNo, String studentName) {
        this.studentNo = studentNo;
        this.studentName = studentName;
    }
 
    //对象的比较涉及到equals()的重写, 这里仅仅比较studentName是否相同
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Student)) return false;
        Student student = (Student) o;
        return studentName.equals(student.getStudentName());
    }
 
    // set()和get()方法均省略..
}

找交集

@Test
public void test(){
 // 1班的学生
 List<Student> class01=new ArrayList<>();
    class01.add(new Student("1","小明"));
    class01.add(new Student("2","赵铁柱")); 
 
 // 2班的学生
 List<Student> class02=new ArrayList<>();
    class02.add(new Student("1","赵铁柱"));

 // 找两个班名字相同的同学(取交集),比较用的是重写的equals()
 List<Student> sameName=class01.stream().filter(class02::contains).collect(Collectors.toList());
 sameName.stream().forEach(student->System.out.println(student.getStudentName()+" "));
 
 //output : 赵铁柱
}

class01.stream().filter(class02::contains)的filter()会 保留 符合表达式的结果,这里面表达式的内容是 2班和1班名字相同的同学。
collect(Collectors.toList())、collect(Collectors.toSet())、collect(Collectors.toMap())将Stream的数据归集到List、Map、Set等集合。

差集
输出结果:b c

@Test
public void test(){
 List<String> list01=Arrays.asList("a","b","c");
    List<String> list02=Arrays.asList("a","e","f");
 
 //list01和list02的差集, 仅保留了 b,c
    List<String> result=list01.stream().filter(word->!list02.contains(word)).collect(Collectors.toList());
    result.stream().forEach(word->System.out.print(word+" "));
}

去重
输出结果:a b c

List<String> list=Arrays.asList("a","b","c","a");
List<String> distinct=list.stream().distinct().collect(Collectors.toList());
distinct.stream().forEach(word->System.out.print(word+" "));

过滤集合,对集合中数据进行二次处理切分

private static List<List<UserInfo>> userInfos = new ArrayList<>();

    @PostConstruct
    public void getUserInfos() {
        if(!environmentConfig.isTest()){
            return ;
        }
        final Map<String, List<CmsConfig>> dict = cmsConfigService.dict(Arrays.asList(DICT_NAME));
        if(MapUtils.isEmpty(dict)){
            return ;
        }
        //获取CmsConfig表中集合数据
        final List<CmsConfig> cmsConfigs = dict.get(DICT_NAME);
        //对CmsConfig表中集合数据中SubCode字段按照“,”切分,然后塞入List<List<UserInfo>> userInfos 集合中
        cmsConfigs.stream().forEach(s -> {
         if(!StringUtils.isEmpty(s.getSubCode())){
            final List<UserInfo> userInfoList = Arrays.stream(s.getSubCode().split(",")).collect(Collectors.mapping(v -> new UserInfo() {{
                setAppid("0");
                setOpenid("0");
                setUserId("0");
                setUserName("0");
                setUnionid("0");
                setPhoneNo(v);
            }}, Collectors.toList()));
            userInfos.add(userInfoList);
          }  
        });
    }

遍历map,过滤筛选

Map<String,String>  moduleNumMap;
		moduleNumMap = itemQueryService.findItemListByClassify("GMV-E").stream().filter(
				ele -> ele.getItemDesc().equals(category)).collect(Collectors.toMap(
						dataKey -> dataKey.getItemDesc(),
				dataValue -> dataValue.getItemAttr()
		));

总之呢,Stream给我们提供了非常大的帮助。只要能够熟练使用的话,确实能够加速开发效率。就会觉得原来写代码居然是那么好玩,easy啦。据说Stream加lamda表达式写的代码简直就是像诗一样优美。

参考文章

https://mp.weixin.qq.com/s/We0lfTr_ahnyVkwxKF0jFw