04 强大的StreamAPI
什么是Stream流:
对于数据源,可以是Collection或是Array,进行一系列的中间操作,然后转化成我们需要的数据.
进行中间操作的对象就是流,流不会改变原来的集合和数组.
数组和集合讲究的是数据,流讲的就是计算:
注意三点:
- Stream自己不存储元素
- Stream不会改变原来的UI想,相反,他们会返回一个持有结果的新Stream
- Stream的操作是延迟的,意味着他们等到需要结果的时候才会执行.
如何操作Stream呢?分为三步:
-
创建流 : 把一个数据源(Collection,Array)获取一个流
-
中间操作 : 一个中间操作链,对数据源数据进行处理
-
终止操作 : 执行中间操作链,并产生结果.
4.1 首先看看创建流:
// 创建流
// 1. 通过Collection的方法创建流
List<String> strList = new ArrayList<>();
Stream<String> listStream = strList.stream();
// 2. 通过Arrays创建流
Employee[] emps = new Employee[10];
Stream<Employee> arrayStream = Arrays.stream(emps);
// 3. 通过Stream的of创建流
Stream<String> streamStream = Stream.of("aaa", "ddd");
// 4. 无限流
// 4.1 迭代
Stream<Integer> iterate = Stream.iterate(0, (x) -> x + 2);
iterate.limit(10).forEach(System.out::println);
// 4.2 生成
Stream<Integer> generate = Stream.generate(() -> new Random().nextInt(100));
generate.limit(10).forEach(System.out::println);
4.2 筛选和切片
filter : 过滤
distinct : 去重
skip : 跳过
limit : 截断流
public class Test {
public static void main(String[] args) {
/**
* 筛选与切片:
* filter--接收Lambda,从流中排除某些元素
* limit--截断流,使其元素不超过给定的数量
* skip(n)--跳过元素,返回一个扔掉了前面n个元素的流,如果元素不超过n个,返回一个空流
* distinct--筛选,通过流中元素的hashCode和equals去掉元素
*/
Employee e1 = new Employee(1,"张三");
Employee e2 = new Employee(2,"李四");
Employee e3 = new Employee(3,"王五");
Employee e4 = new Employee(3,"王五");
List<Employee> lists = new ArrayList<>(3);
lists.add(e1);
lists.add(e2);
lists.add(e3);
lists.add(e4);
lists.stream().filter(e -> e.getId() > 1)
.limit(3).skip(1).distinct()
.forEach(System.out::println);
}
}
@Data
class Employee {
private Integer id;
private String name;
public Employee() {
}
public Employee(Integer id, String name) {
this.id = id;
this.name = name;
}
public Employee(Integer id) {
this.id = id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return Objects.equals(id, employee.id) &&
Objects.equals(name, employee.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
}
4.3 映射
public class Test {
public static void main(String[] args) {
/**
* 映射:
* map--接收Lambda,将元素转换为其他形式或是提取信息,接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射为一个新的元素
* flatMap--接收一个函数作为参数,将流中的每一个元素都转换成另外一个流,然后把所有流连接成一个新的流
*/
Employee e1 = new Employee(1,"张三");
Employee e2 = new Employee(2,"李四");
Employee e3 = new Employee(3,"王五");
Employee e4 = new Employee(3,"王五");
List<Employee> lists = new ArrayList<>(3);
lists.add(e1);
lists.add(e2);
lists.add(e3);
lists.add(e4);
// 需求1 : 根据这个集合输出所有人的名字
lists.stream().map( employee -> employee.getName())
.distinct()
.forEach(System.out::println);
System.out.println("----------------------------------------");
// 需求2 : 根据这个集合输出所有人名字的字符,flatMap是将输入的数据转换为一个流,并将这个流和其他流合并起来
lists.stream().flatMap(employee -> {
char[] chars = employee.getName().toCharArray();
List<Character> characters = new ArrayList<>();
for (char aChar : chars) {
characters.add(aChar);
}
return characters.stream();
}).distinct().forEach(System.out::println);
}
}
@Data
class Employee {
private Integer id;
private String name;
public Employee() {
}
public Employee(Integer id, String name) {
this.id = id;
this.name = name;
}
public Employee(Integer id) {
this.id = id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return Objects.equals(id, employee.id) &&
Objects.equals(name, employee.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
}
4.4 排序
public class Test {
public static void main(String[] args) {
/**
* 排序:
* sorted() -- 自然排序
* sorted(Comparator com) -- 定制排序
*/
List<String> strLists = new ArrayList<>();
strLists.add("aa");
strLists.add("vv");
strLists.add("xx");
strLists.add("33");
strLists.stream().sorted().forEach(System.out::println);
System.out.println("-------------------------");
List<Employee> employeeList = new ArrayList<>();
Employee e1 = new Employee(1, "张三");
Employee e2 = new Employee(2, "李四");
Employee e3 = new Employee(3, "王五");
Employee e4 = new Employee(3, "王五");
employeeList.add(e1);
employeeList.add(e2);
employeeList.add(e3);
employeeList.add(e4);
employeeList.stream().
sorted((x, y) -> x.getName().hashCode() - y.getName().hashCode()).
forEach(x -> System.out.println(x.getName()));
}
}
@Data
class Employee {
private Integer id;
private String name;
public Employee() {
}
public Employee(Integer id, String name) {
this.id = id;
this.name = name;
}
public Employee(Integer id) {
this.id = id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return Objects.equals(id, employee.id) &&
Objects.equals(name, employee.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
}
4.5 StreamAPI的终止操作--查找与匹配
- allMatch--检查是否匹配所有元素
- anyMatch--检查是否至少匹配一个元素
- noneMatch--检查是否没有匹配所有元素
- findFirst--返回第一个元素
- findAny--返回流中的任意元素
- count--返回元素个数
- max--返回流中最大值
- min--返回流中最小值
List<Employee> employeeList = new ArrayList<>();
Employee e1 = new Employee(1, "张三");
Employee e2 = new Employee(2, "李四");
Employee e3 = new Employee(3, "王五");
Employee e4 = new Employee(3, "王五");
employeeList.add(e1);
employeeList.add(e2);
employeeList.add(e3);
employeeList.add(e4);
boolean b = employeeList.stream().allMatch(employee -> employee.getId() > 0);
boolean c = employeeList.stream().anyMatch(employee -> employee.getName().equals("张三"));
boolean d = employeeList.stream().noneMatch(employee -> employee.getName().equals("六大"));
Optional<Employee> first = employeeList.stream().findFirst();
Employee employee = first.get();
Optional<Employee> any = employeeList.stream().findAny();
Employee employee1 = any.get();
long count = employeeList.stream().count();
Optional<Employee> max = employeeList.stream().max((x, y) -> x.getName().length() - y.getName().length());
Optional<Employee> min = employeeList.stream().min((x, y) -> x.getName().length() - y.getName().length());
4.6 归约和收集
归约 reduce(T identity,BinaryOperator) / reduce(BinaryOperator) 将流中的元素结合起来,得到一个值
List<Employee> employeeList = new ArrayList<>();
Employee e1 = new Employee(1, "张三");
Employee e2 = new Employee(2, "李四");
Employee e3 = new Employee(3, "王五");
Employee e4 = new Employee(3, "王五");
employeeList.add(e1);
employeeList.add(e2);
employeeList.add(e3);
employeeList.add(e4);
// 统计名字的个数
Stream<Map<Character, Integer>> mapStream = employeeList.stream().flatMap(employee -> {
Map<Character, Integer> hashMap = new HashMap<>();
String name = employee.getName();
char[] chars = name.toCharArray();
for (char aChar : chars) {
Integer integer = hashMap.get(aChar);
if (integer == null) {
hashMap.put(aChar, 1);
} else {
hashMap.put(aChar, integer + 1);
}
}
return Stream.of(hashMap);
});
Optional<Map<Character, Integer>> reduce = mapStream.reduce((x, y) -> {
Map<Character, Integer> result = new HashMap<>();
x.keySet().forEach( z -> {
result.put(z,x.get(z));
});
y.keySet().forEach( v -> {
if (result.get(v) != null) {
result.put(v,result.get(v) + 1);
}else {
result.put(v,y.get(v));
}
});
return result;
}
);
Map<Character, Integer> characterIntegerMap = reduce.get();
System.out.println(characterIntegerMap);
}
}
收集: collect--将流转换为其他形式,接收一个Collect接口来实现,用于Stream中元素汇总
/**
收集: collect(Collector c) 将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总方法
toList: 把流元素收集到List
toSet:
toCollection:
counting
summingInt
averageingInt
summarizingInt: 收集流中Integer属性的值,比如平均值
joining: 连接流中的字符串
maxBy : 根据比较器选择最大值
minBy : 根据比较器选择最小值
reducing: 归约产生类型
collectingAndThen : 包括另外一个收集器
groupingBy : 分组
partitionBy : 分区
*/
List<Employee> employeeList = new ArrayList<>();
Employee e1 = new Employee(1, "张三");
Employee e2 = new Employee(2, "李四");
Employee e3 = new Employee(3, "王五");
Employee e4 = new Employee(4, "王五");
employeeList.add(e1);
employeeList.add(e2);
employeeList.add(e3);
employeeList.add(e4);
List<String> collect1 = employeeList.stream().map((x) -> x.getName()).collect(Collectors.toList());
Set<String> collect2 = employeeList.stream().map((x) -> x.getName()).collect(Collectors.toSet());
LinkedList<String> collect = employeeList.stream().map((x) -> x.getName()).collect(Collectors.toCollection(() -> new LinkedList<>()));
Long collect3 = employeeList.stream().collect(Collectors.counting());
Double collect4 = employeeList.stream().collect(Collectors.averagingInt(x -> x.getId()));
String collect5 = employeeList.stream().map(x -> x.getName()).collect(Collectors.joining());
Optional<Integer> collect6 = employeeList.stream().map(x -> x.getId()).collect(Collectors.maxBy((x, y) -> x - y));
Map<String, List<Employee>> collect7 = employeeList.stream().collect(Collectors.groupingBy(x -> x.getName()));
System.out.println(collect7);
Map<Boolean, List<Employee>> collect8 = employeeList.stream().collect(Collectors.partitioningBy(x -> x.equals("王五")));
System.out.println(collect8);
}
5 并行流与串行流
并行流就是把一个数据块分成了多个数据块,并且用了不同呢的线程分别处理每个数据块的流.
StreamAPI里面可以通过parallel() 和 sequential() 进行并行流和顺序流的切换.
并行流底层是采用的Fork/Join框架.就是将大任务拆分成小任务,然后小任务进行并行的计算,然后将部分结果进行合并.
Fork/Join采用了工作窃取的模式.
6 Optional类: 尽可能的避免空指针异常
常用方法:
Optional.of(T t) : 创建一个 Optional 实例
Optional.empty() : 创建一个空的 Optional 实例
Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例
isPresent() : 判断是否包含值
orElse(T t) : 如果调用对象包含值,返回该值,否则返回t
orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回 s 获取的值
map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()
flatMap(Function mapper):与 map 类似,要求返回值必须是Optional
7. 接口中默认的方法和静态方法
接口可以实现默认的方法使用default修饰符进行修饰,但是也会存在问题.
同时JAVA8里面接口支持静态方法.
问题1: 如果接口的默认方法和抽象类的方法名相同,一个子类同时继承抽象类和实现了接口,那么就是类优先原则,子类实际上是继承的父类的同名方法.
问题2: 如果两个接口都有一个同名的默认方法,如果一个子类同时实现了两个接口,那么子类就必须覆盖同名方法.
网友评论