流式编程
一、流支持
java中代码那么多,如何简单的让大多数类库中的类都引入流的概念呢?
Java 8 采用的解决方案是:在接口中添加被 default(默认)修饰的方法。通过这种方案,设计者们可以将流式(stream)方法平滑地嵌入到现有类中。流方法预置的操作几乎已满足了我们平常所有的需求。流操作的类型有三种:创建流,修改流元素(中间操作, Intermediate Operations),消费流元素(终端操作, Terminal Operations)。最后一种类型通常意味着收集流元素(通常是汇入一个集合)。
二、创建流
package com.gykalc.jdk8.stream;
import java.util.*;
import java.util.stream.Stream;
public class StreamTest1 {
public static void main(String[] args) {
// 创建流的方式
/**
* 创建一个无限大小的流,通常使用limit去限制大小
* @params Object seed:表示传入的第一个值
* @params UnaryOperator:是一个函数式接口,继承了Function,传入一个值,返回一个值,两个值类型相同
*/
Stream.iterate(1, i -> i + 2).limit(5).forEach(System.out::print);// 13579
Random random = new Random(47);
/**
* 方法返回一个无限连续的无序流,用来生成随机流或常量流,也可以用limit限制大小
*/
Stream.generate(() -> random.nextInt(20)).limit(5).forEach(System.out::println);
/**
* 通过一个可变参数,传入多个值创建的一个流对象
*/
Stream.of(1, 3, 5, 7, 9).forEach(System.out::println);
/**
* 创建一个空的流对象
*/
Stream.empty();
}
}
除此之外,每个集合都可以通过调用 stream() 方法来产生一个流。代码示例:
import java.util.*;
import java.util.function.Function;
import java.util.stream.Stream;
public class Main {
}
class CollectionToStream {
public static void main(String[] args) {
List<Bubble> bubbleList = Arrays.asList(new Bubble(1), new Bubble(2), new Bubble(3));
System.out.println(bubbleList.stream()
.mapToInt(b -> b.i)
.sum());
Set<String> w = new HashSet<>(Arrays.asList("It's a wonderful day for pie!"));
w.stream().
map(x -> x + " ")
.forEach(System.out::println);
Map<String, Double> m = new HashMap<>();
m.put("pi", 3.14159);
m.put("e", 2.718);
m.put("phi", 1.618);
m.entrySet().stream()
.map(e -> e.getKey() + ": " + e.getValue())
.forEach(System.out::println);
}
}
class Bubble {
public final int i;
public Bubble(int n) {
i = n;
}
@Override
public String toString() {
return "Bubble{" +
"i=" + i +
'}';
}
private static int count = 0;
public static Bubble bubbler() {
return new Bubble(count++);
}
}
三、函数式接口
还有很多这种函数式接口,我们可以进入源代码中查看他的作用
四、Optional的作用
主要用来避免空指针异常
package com.gykalc.jdk8.stream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class StreamTest1 {
public static void main(String[] args) {
// 创建一个空的Optional
Optional<Object> empty = Optional.empty();
// 创建一个Optional对象
Optional<Integer> integer = Optional.of(2);
// 创建一个包含允许是空元素的Optional对象
Optional<Object> o = Optional.ofNullable(null);
// 判断Optional中的元素是否为null
System.out.println(empty.isPresent());
System.out.println(integer.isPresent());
System.out.println(o.isPresent());
// orElse:如果Optional中的元素为空,则返回other参数
System.out.println(integer.orElse(1));
System.out.println(empty.orElse(1));
System.out.println(o.orElse("1"));
// get:获取Optional中的元素,如果为null,会报错
// empty.get();
// ifPresent:如果Optional的元素不为空,就执行里面的函数或者lambda
empty.ifPresent(System.out::println);
integer.ifPresent(System.out::println);
o.ifPresent(System.out::println);
List<Integer> list = new ArrayList<>(Arrays.asList(1, 3, 5, 7, 9));
Optional<List<Integer>> optionalList = Optional.of(list);
// flatMap:flatMap中的函数,返回值必须是Optional<T>类型的
Optional<Integer> integer1 = optionalList.flatMap(param -> param.stream().filter(i -> i > 10).findFirst());
System.out.println(integer1);
// map:map中的函数,返回值是任意类型,但是返回之后会被map包装为一个optional对象
Optional<Integer> integer2 = optionalList.map(param -> param.stream().filter(i -> i > 10).findFirst().orElse(null));
System.out.println(integer2);
}
}
五、使用流操作集合,完成以前的复杂操作
package com.gykalc.jdk8.stream;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StreamTest1 {
public static void main(String[] args) {
Student student1 = new Student("zhangsan", 100, 20);
Student student2 = new Student("lisi", 90, 21);
Student student3 = new Student("wangwu", 91, 22);
Student student4 = new Student("zhaoliu", 80, 21);
Student student5 = new Student("zhangsan", 92, 20);
List<Student> studentList = new ArrayList<>(Arrays.asList(student1, student2, student3, student4, student5));
/**
* 流操作中,返回Stream对象的都是中间操作,懒加载。
* 不返回Stream对象的是终止操作,会立即执行所有操作
* 而且在流中,一个流不能重复被操作,因为一旦操作流,就
* 会生成一个新的流对象
* map:将studentList中所有元素都遍历一遍,
* 并执行某个函数操作,他是中间操作,懒加载的,
* 一定要遇到终止操作才会执行
*
* 使用map,将zhangsan的名字改成"张三"
*/
studentList.stream().map(stu -> {
if (stu.getName().equals("zhangsan")) {
stu.setName("张三");
}
return stu;
}).forEach(System.out::println);
// 多重集合
List<List<Integer>> lists = Arrays.asList(Arrays.asList(1), Arrays.asList(2, 3), Arrays.asList(4, 5, 6));
/**
* 如果我们想把多重集合的值,全部都转移到一个集合中,可以使用flatmap
*/
List<Integer> integerList = lists.stream().flatMap(l -> l.stream()).collect(Collectors.toList());
System.out.println(integerList);
/**
* 我们想要把所有的学生名称存到一个集合中,也可以使用flatmap
*/
List<String> nameList = studentList.stream().flatMap(stu -> Stream.of(stu.getName())).collect(Collectors.toList());
System.out.println(nameList);
/**
* 假如我们想要给学生按名称进行分组,并保存到一个map中
* 结构为:Map<String, List<Student>>
* 方法一:
*/
HashMap<String, List<Student>> stringListHashMap1 = studentList.stream().collect(HashMap<String, List<Student>>::new,
(hashMap, student) -> {
if (hashMap.containsKey(student.getName())) {
hashMap.get(student.getName()).add(student);
} else {
hashMap.put(student.getName(), new ArrayList<>(Arrays.asList(student)));
}
}, (stringListHashMap, stringListHashMap2) -> stringListHashMap.putAll(stringListHashMap));
System.out.println(stringListHashMap1);
/**
* 假如我们想要给学生按名称进行分组,并保存到一个map中
* 结构为:Map<String, List<Student>>
* 方法二:
*/
Map<String, List<Student>> stringListHashMap2 = studentList.stream().collect(Collectors.groupingBy(Student::getName));
System.out.println(stringListHashMap2);
/**
* 假如我们想要给学生按名称进行分组获取成绩的平均值
*/
Map<String, Double> stringDoubleMap = studentList.stream().collect(Collectors.groupingBy(Student::getName, Collectors.averagingDouble(Student::getScore)));
System.out.println(stringDoubleMap);
/**
* 分区:分组的特殊情况,只存在true和false
* 将分数大于90的和小于90的分区
*/
Map<Boolean, List<Student>> booleanListMap = studentList.stream().collect(Collectors.partitioningBy(stu -> stu.getScore() > 90));
System.out.println(booleanListMap);
}
}