Java技术栈
www.javastack.cn
关注阅读更多优质文章
作者:funnyZpC出处:cnblogs.com/funnyzpc/p/10382053.html
Stream是一类用于替代对集合操作的工具类+Lambda式编程,他可以替代现有的遍历、过滤、求和、求最值、排序、转换等
并行方式parallelStream
顺序方式Stream
Lambda 可有效减少冗余代码,减少开发工作量
内置对集合List、Map的多种操作方式,含基本数据类型处理
并行Stream有效率优势(内置多线程)
遍历forEach
@Test public void stream() { //操作List List<Map<String, String>> mapList = new ArrayList() { { Map<String, String> m = new HashMap(); m.put("a", "1"); Map<String, String> m2 = new HashMap(); m2.put("b", "2"); add(m); add(m2); } }; mapList.stream().forEach(item-> System.out.println(item)); //操作Map Map<String,Object> mp = new HashMap(){ { put("a","1"); put("b","2"); put("c","3"); put("d","4"); } }; mp.keySet().stream().forEachOrdered(item-> System.out.println(mp.get(item))); }过滤filter
List<Integer> mapList = new ArrayList() { { add(1); add(10); add(12); add(33); add(99); } }; //mapList.stream().forEach(item-> System.out.println(item)); mapList = mapList.stream().filter(item->{ return item>30; }).collect(Collectors.toList()); System.out.println(mapList);转换map和极值
@Test public void trans(){ List<Person> ps = new ArrayList<Person>(){ { Person p1 = new Person(); p1.setAge(11); p1.setName("张强"); Person p2 = new Person(); p2.setAge(17); p2.setName("李思"); Person p3 = new Person(); p3.setAge(20); p3.setName("John"); add(p1); add(p2); add(p3); } }; //取出所有age字段为一个List List<Integer> sumAge = ps.stream().map(Person::getAge).collect(Collectors.toList()); System.out.println(sumAge); //取出age最大的那 Integer maxAge =sumAge.stream().max(Integer::compare).get(); System.out.println(maxAge); } class Person{ private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }模拟耗时简单业务逻辑
public class DataLoopBlockTest { private static final Logger LOG= LoggerFactory.getLogger(DataLoopTest.class); private static final List<Person> persons = new ArrayList<>(); static { for(int i=0;i<=100000;i++){ Person p = new Person(); p.setAge(i); p.setName("zhangSan"); p.setJoinDate(new Date()); persons.add(p); } } /** * for 循环耗时 ===> 101.385 * for 循环耗时 ===> 102.161 * for 循环耗时 ===> 101.472 * */ @Test public void forTest(){ Instant date_start = Instant.now(); int personSize = persons.size(); for(int i=0;i<personSize;i++){ try { Thread.sleep(1); persons.get(i).setLabel(persons.get(i).getName().concat("-"+persons.get(i).getAge()).concat("-"+persons.get(i).getJoinDate().getTime())); }catch (Exception e){ e.printStackTrace(); } } Instant date_end = Instant.now(); LOG.info("for 循环耗时 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0); } /** * forEach 循环耗时 ===> 101.027 * forEach 循环耗时 ===> 102.488 * forEach 循环耗时 ===> 101.608 */ @Test public void forEach(){ Instant date_start = Instant.now(); for(Person p:persons){ try { Thread.sleep(1); p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime())); }catch (Exception e){ e.printStackTrace(); } } Instant date_end = Instant.now(); LOG.info("forEach 循环耗时 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0); } /** * streamForeach 循环耗时 ===> 103.246 * streamForeach 循环耗时 ===> 101.128 * streamForeach 循环耗时 ===> 102.615 */ @Test public void streamForeach(){ Instant date_start = Instant.now(); //persons.stream().forEach(p->p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime()))); persons.stream().forEach(p->{ try { Thread.sleep(1); p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime())); }catch (Exception e){ e.printStackTrace(); } }); Instant date_end = Instant.now(); LOG.info("streamForeach 循环耗时 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0); } /** * parallelStreamForeach 循环耗时 ===> 51.391 * parallelStreamForeach 循环耗时 ===> 53.509 * parallelStreamForeach 循环耗时 ===> 50.831 */ @Test public void parallelStreamForeach(){ Instant date_start = Instant.now(); //persons.parallelStream().forEach(p->p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime()))); persons.parallelStream().forEach(p->{ try { Thread.sleep(1); p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime())); }catch (Exception e){ e.printStackTrace(); } }); Instant date_end = Instant.now(); LOG.info("parallelStreamForeach 循环耗时 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0); //LOG.info("\r\n===> {}",JSON.toJSONString(persons.get(10000))); } }可以看到在百万数据下做简单数据循环处理,对于普通for(for\foreach)循环或Stream(并行、非并行)下,几者的效率差异并不明显。
注意: 在百万数据下,普通for、foreach循环处理可能比Stream的方式快许多,对于这点效率的损耗,其实lambda表达式对代码的简化更大!
另外,在并行流的循环下速度提升了一倍之多,当单个循环耗时较多时,会拉大与前几者的循环效率 (以上测试仅对于循环而言,其他类型业务处理,比如排序、求和、最大值等未做测试,个人猜测与以上测试结果相似)
并行Stream不是线程安全的,当对循坏外部统一对象进行读写时候会造成意想不到的错误,这需要留意
因Stream总是惰性的,原对象是不可以被修改的,在集合处理完成后需要将处理结果放入一个新的集合容器内
普通循环与Stream(非并行)循环,在处理处理数据量比较大的时候效率是一致的,推荐使用Stream的形式
对于List删除操作,目前只提供了removeIf方法来实现,并不能使用并行方式
对于lambda表达式的写法
当表达式内只有一个返回boolean类型的语句时候语句是可以简写的,例如:
persons.parallelStream().forEach(p->p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime())));当表达式内会有一些复杂处理逻辑时需要加上大括号,这与初始化List参数方式大致一致
try { Thread.sleep(1); p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime())); }catch (Exception e){ e.printStackTrace(); } });流到流之间的转换类
filter(过滤)
map(映射转换)
mapTo[Int|Long|Double] (到基本类型流的转换)
flatMap(流展开合并)
flatMapTo[Int|Long|Double]
sorted(排序)
distinct(不重复值)
peek(执行某种操作,流不变,可用于调试)
limit(限制到指定元素数量)
skip(跳过若干元素)
流到终值的转换类
toArray(转为数组)
reduce(推导结果)
collect(聚合结果)
min(最小值)
max(最大值)
count (元素个数)
anyMatch (任一匹配)
allMatch(所有都匹配)
noneMatch(一个都不匹配)
findFirst(选择首元素)
findAny(任选一元素)
直接遍历类
forEach (不保证顺序遍历,比如并行流)
forEachOrdered(顺序遍历)
构造流类
empty (构造空流)
of (单个元素的流及多元素顺序流)
iterate (无限长度的有序顺序流)
generate (将数据提供器转换成无限非有序的顺序流)
concat (流的连接)
Builder (用于构造流的Builder对象)
好了,本篇到这里了,如果你想阅读更多 Java 8 + 系列教程,可以关注公众号Java技术栈回复 java 获取,我都写了一大堆了。
关注Java技术栈看更多干货
戳原文,获取精选面试题!