- [摘要]
java8已经推出很久时间了,其中最令人振奋的要数lambda表达式和stream这两个特性了。这篇文章主要讲一下关于java8中Stream的相关内容。
首先,流是java8的一大亮点,它用来专注于对容器对象进行各种便利的、高效的聚合操作,或者大批量的数据操作。Stream API
结合一起出现的lambda表达式,极大地提升了编程效率和可读性。
- [ 聚合操作的概念]
- 什么是聚合操作?
有过java开发经验的童鞋就非常清楚,我们平时在传统的j2ee应用中,java代码通常要通过关系型数据库的聚合操作来完成一些事情。如: 总金额计算,平均值计算,最大值最小值,本周或者本月完成的有效订单等等。
这类操作在现今数据爆炸的社会,我们如果总是要通过数据库来完成(java代码中往往是通过遍历,或者iterator,显然显得很笨重),所以stream的出现,也可以说是应运而生。
在Java7中,如果要发现type为grocery的所有交易,然后返回以交易值降序排序好的交易ID集合,我们需要这样写:
List<Transaction> groceryTransactions = new Arraylist<>();
for(Transaction t: transactions){ if(t.getType() ==
Transaction.GROCERY){ groceryTransactions.add(t); } }
Collections.sort(groceryTransactions, new Comparator(){ public int
compare(Transaction t1, Transaction t2){ return
t2.getValue().compareTo(t1.getValue()); } });
List<Integer> transactionIds = new ArrayList<>(); for(Transaction t:
groceryTransactions){ transactionsIds.add(t.getId()); }
可是在java8中,我们可以使用Stream,代码将会变得更加简单易读,甚至可以使用并发模式,提高程序运行效率。如下:
List<Integer> transactionsIds = transactions.parallelStream()
.filter(t -> t.getType() == Transaction.GROCERY)
.sorted(comparing(Transaction::getValue).reversed())
.map(Transaction::getId).collect(toList());
怎么样?是不是很简单?
这正是java官方推出的新模式。大家都知道,java是一门强逻辑语言,要求语法严密,细节到位,这往往会带来语言本身上的一些劣势,比如代码繁多,琐碎,不利于长期发展。尤其是如今python、php等等语言异军突起。相比而言,java的门槛和难度都相对较高。stream和lambda表达式、以及函数接口,函数式变成等思想,都是从别的语言中借鉴而来的。java是一个时代的产物,自然也要与时俱进。而在这样的背景下,我们所要做的,也是顺应时代的发展,好好去学习这些新东西。
言归正传,让我们来简单了解一下,java8一些比较典型的专注聚合操作的api。
- [ Stream API]
①anyMatch()、allMatch()、noneMatch()操作,从字面上,我们可以猜出,他们大概是match——匹配的意思,然而事实上,也确实是如此。这三个都是函数式接口,返回值是boolean。这也是java的新特性之一,如果了解函数式接口的定义,请参看这一篇大神的博客—https://blog.csdn.net/qq_28410283/article/details/80601495
那么,这里说下具体用途:
anyMatch——表示集合中的所有元素,只要有任意一个符合要求的,那么就返回true。
allMatch—— 表示集合中所有的元素,都要满足要求,才会返回true,否则返回false。
noneMatch——这个就跟allMatch刚好相反,集合里面的元素,刚好全部都不满足条件,返回true,否则则返回false。举例子如下:
List<String> strs = Arrays.asList("a", "a", "a", "a", "b");
boolean aa = strs.stream().anyMatch(str -> str.equals("a"));
boolean bb = strs.stream().allMatch(str -> str.equals("a"));
boolean cc = strs.stream().noneMatch(str -> str.equals("a"));
long count = strs.stream().filter(str -> str.equals("a")).count();
System.out.println(aa);// TRUE
System.out.println(bb);// FALSE
System.out.println(cc);// FALSE
System.out.println(count);// 4
② distinct(),empty()方法,这两个方法也能从字面上了解意思。distinct()用户对集合去重。empty()则是创建一个空流。
String[] strings = {"a", "a", "b", "c", "d", "d"};
List<String> list = Arrays.asList(strings);
String collect = list.stream().distinct().collect(Collectors.joining(","));
//空流 ,一个空的顺序Stream,该Stream里面不包含元素项
Stream<Object> empty = Stream.empty();//emmmmm,至于空Stream有啥作用,从api上我也看不出来,希望有高手点拨
③filter()、map()方法,这两个方法是中间处理过程的方法。
filter()是在对集合每个元素之间进行一道逻辑处理程序(可以看做是一个过滤器,事实上通常的运用也就是来做元素过滤的)。
而map()方法则是一个映射方法,这个方法经常会在两个不同对象集合中进行对象的转换和组装的时候用到。比如,讲一个List 转化为 List 中。person肯定包含women,但是在转化为women的时候,也要进行一道工序。这个时候就可以使用map()方法。此方法的好处是可以针对一个集合直接进行转化,而不需要遍历。
//person类list中,去掉年龄超过50岁的,可以使用filter()方法轻松实现
List<Person> youngPersonList = allPersonList.Stream().filter(item -> item.getAge()<50).collect(Collectors.toList());
//convertPersonToWomen是Person转化为Women的业务方法
List<Women> womens=person.stream().map(StreamMap::convertPersonToWomen)
.collect(Collectors.toList())
④mapToInt(),mapTolong(),mapToDouble() ,这三个方法是属于同一性质的。用户将list中的元素的对应类型的属性值聚集成某种特定的类型的stream。在这个基础上,我们可以对这个stream进行自己想要的操作。比如求平均值,求和等等。
//对people的年龄属性求和
int sum = people.stream()
.mapToInt(p -> p.getAge())
.sum();
System.out.println("Total of ages " + sum);
//对people的年龄属性求平均数
OptionalDouble avg = people.stream()
.mapToInt(p -> p.getAge())
.average();
if (avg.isPresent()) {
System.out.println("Average: " + avg);
} else {
System.out.println("average wasn't calculated");
}