Stream in Java8
简单认识Java stream
Java8出了一个stream流式编程,在开发中或多或少用到接触过。怎么说呢!举个例子把,一起我们在遍历一个集合的时候,我们是从外部去遍历的,然后才能拿到结果,这样来效率就会变得相对低一点。而这个时候我们去内部去遍历集合的时候,直接从内部拿数据。减少资源消耗,提升效率。
什么是stream呢?
Stream它并不是一个容器,它只是对容器的功能进行了增强,添加了很多便利的操作,例如查找、过滤、分组、排序等一系列的操作。并且有串行、并行两种执行模式,并行模式充分的利用了多核处理器的优势,使用fork/join框架进行了任务拆分,同时提高了执行速度。简而言之,Stream就是提供了一种高效且易于使用的处理数据的方式。
首先我们来看下简单的一个执行流程
从这简单的图可以看出,总共就只有三步,相对来说还是比较容易接受。第一步是创建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);
}
}
以上就是我们我们之前常用的三种遍历方式,可能大家更加倾向于这三种,因为我们在平时开发中或者自己练习的时候。用的比较多,也就慢慢就接受了。但是如果数据量一大?大量数据进行遍历的时候那个这效率,就变得低起来了。所以,steam就出来了。首先我们看看code:
创建Stream
//look code
public static void main (String[] args){
List<String> sList = Arrays.asList("zhangsan","heqing","lisi","...");
//创建顺序流
java.util.stream.Stream<String> stream = sList.stream();
//创建并接流
java.util.stream.Stream<String> stringStream = sList.parallelStream();
stream.forEach(System.out::println);
}
//Arrays.asList
//stream
//parallelStream
stream是顺序流,由主线程按顺序对流执行操作,而parallelStream是并行流,内部以多线程并行执行的方式对流进行操作,需要注意使用并行流的前提是流中的数据处理没有顺序要求(会乱序,即使用了forEachOrdered)。
当然我们也可以去创建一个顺序流
//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)
在stream中提供了很多方法比如filter;
在以前我们要去一个集合或者数组中筛选我们想要的数据。还要进行一系列的操作,首先创建数组或者集合,然后遍历 然后判断,然后写逻辑等等。。。很麻烦
public static void main(String [] args){
List list= new ArrayList<>();
list.add();
....
for(){
....
....
...
逻辑代码等。。。。
}
}
但是如果用到了stream后就没不会有这么判断条件。切代码量小了,效率高了。谁又不想少写代码呢?早点下班不香吗?
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)
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));
}
flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
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);
}
聚合(max/min/count)
max、min、count这些字眼你一定不陌生,没错,在mysql中我们常用它们进行数据统计。Java stream中也引入了这些概念和用法,极大地方便了我们对集合、数组的数据统计工作。
max获取String集合中最长的元素
public class StreamTest {
public static void main(String[] args) {
List<String> list = Arrays.asList("adnm", "admmt", "pot", "xbangd", "weoujgsd");
Optional<String> max = list.stream().max(Comparator.comparing(String::length));
System.out.println("最长的字符串:" + max.get());
}
}
收集(collect)
collect,收集,可以说是内容最繁多、功能最丰富的部分了。从字面上去理解,就是把一个流收集起来,最终可以是收集成一个值也可以收集成一个新的集合。
归集(toList/toSet/toMap)
因为流不存储数据,那么在流中的数据完成处理后,需要将流中的数据重新归集到新的集合里。toList、toSet和toMap比较常用,另外还有toCollection、toConcurrentMap等复杂一些的用法。
下面用一个案例演示toList、toSet和toMap:
public class StreamTest {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 6, 3, 4, 6, 7, 9, 6, 20);
List<Integer> listNew = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toList());
Set<Integer> set = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toSet());
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom", 8900, 23, "male", "New York"));
personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
personList.add(new Person("Anni", 8200, 24, "female", "New York"));
Map<?, Person> map = personList.stream().filter(p -> p.getSalary() > 8000)
.collect(Collectors.toMap(Person::getName, p -> p));
System.out.println("toList:" + listNew);
System.out.println("toSet:" + set);
System.out.println("toMap:" + map);
}
}
统计(count/averaging)
Collectors提供了一系列用于数据统计的静态方法:
计数:count
平均值:averagingInt、averagingLong、averagingDouble
最值:maxBy、minBy
求和:summingInt、summingLong、summingDouble
统计以上所有:summarizingInt、summarizingLong、summarizingDouble
public class StreamTest {
public static void main(String[] args) {
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom", 8900, 23, "male", "New York"));
personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
// 求总数
Long count = personList.stream().collect(Collectors.counting());
// 求平均工资
Double average = personList.stream().collect(Collectors.averagingDouble(Person::getSalary));
// 求最高工资
Optional<Integer> max = personList.stream().map(Person::getSalary).collect(Collectors.maxBy(Integer::compare));
// 求工资之和
Integer sum = personList.stream().collect(Collectors.summingInt(Person::getSalary));
// 一次性统计所有信息
DoubleSummaryStatistics collect = personList.stream().collect(Collectors.summarizingDouble(Person::getSalary));
System.out.println("员工总数:" + count);
System.out.println("员工平均工资:" + average);
System.out.println("员工工资总和:" + sum);
System.out.println("员工工资所有统计:" + collect);
}
}
排序(sorted)
sorted,中间操作。有两种排序:
- sorted():自然排序,流中元素需实现Comparable接口
- sorted(Comparator com):Comparator排序器自定义排序
public class StreamTest {
public static void main(String[] args) {
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Sherry", 9000, 24, "female", "New York"));
personList.add(new Person("Tom", 8900, 22, "male", "Washington"));
personList.add(new Person("Jack", 9000, 25, "male", "Washington"));
personList.add(new Person("Lily", 8800, 26, "male", "New York"));
personList.add(new Person("Alisa", 9000, 26, "female", "New York"));
// 按工资升序排序(自然排序)
List<String> newList = personList.stream().sorted(Comparator.comparing(Person::getSalary)).map(Person::getName)
.collect(Collectors.toList());
// 按工资倒序排序
List<String> newList2 = personList.stream().sorted(Comparator.comparing(Person::getSalary).reversed())
.map(Person::getName).collect(Collectors.toList());
// 先按工资再按年龄升序排序
List<String> newList3 = personList.stream()
.sorted(Comparator.comparing(Person::getSalary).thenComparing(Person::getAge)).map(Person::getName)
.collect(Collectors.toList());
// 先按工资再按年龄自定义排序(降序)
List<String> newList4 = personList.stream().sorted((p1, p2) -> {
if (p1.getSalary() == p2.getSalary()) {
return p2.getAge() - p1.getAge();
} else {
return p2.getSalary() - p1.getSalary();
}
}).map(Person::getName).collect(Collectors.toList());
System.out.println("按工资升序排序:" + newList);
System.out.println("按工资降序排序:" + newList2);
System.out.println("先按工资再按年龄升序排序:" + newList3);
System.out.println("先按工资再按年龄自定义降序排序:" + newList4);
}
}
总之呢,stream给我们提供了非常大的帮助。只要能够熟练使用的话,确实能够加速开发效率。就会觉得原来写代码居然是那么好玩,easy啦。据说stream加lamda表达式写的代码简直就是像诗一样优美。不过我不知道什么是美。反正lamda用着挺不习惯的。