Java8 函数式编程
在思考问题时,使用不可变值和函数,函数对一个值进行处理,映射成另一个值。
系统的功能应该更多的源自于程序之间的关系,而非程序本身。
lambda表达式
- lambda 的几种形式
Runnable noArg = () -> System.out.println("111");
ActionListener oneArg = event -> System.out.println("222");
Runnable multisStatemnet = () -> {
System.out.println("333");
System.out.println("444");
};
BinaryOperator<Long> add = (x, y) -> x * y;
BinaryOperator<Long> addExp = (Long x, Long y) -> x + y;
// 目标类型是指Lambda表达式所在上下文环境的类型。
// lambda表达式的类型依赖上下文环境
- Lambda表达式是一个匿名方法,将行为像数据一样进行传递。
- 函数接口指仅具有单个抽象方法的接口,用来表示Lambda表达式的类型。
- 可以不再显式生命类型,Java8会进行类型推断。
匿名内部类
内部类创建表达式会确保在创建一个拥有唯一标识的新对象,而lambda可能有,也可能没有,这取决与具体表现,相对于内部类来说,这种灵活性可以让平台使用更为高效的实现策略。
内部类的声明会创建出一个新的命名作用域,在这个作用域中,this和super指的是内部类本身的当前实例;相反lambda表达式并不会引入任何新的命名环境,这样避免了内部类名称查找的复杂性。
流
如果数据的处理结果和顺序没有关系,那么可以采用并行的计算模型。
- 常用的流操作
@Data
@Builder
public class Student {
private String name;
private Integer sex;
private Integer age;
}
// 构建一个学生和课程成绩的关系,来测试lambda表达式
List<Student> studentList = new ArrayList<>();
studentList.add(Student.builder().name("aa").age(11).sex(1).build());
studentList.add(Student.builder().name("bb").age(12).sex(0).build());
studentList.add(Student.builder().name("cc").age(10).sex(1).build());
studentList.add(Student.builder().name("dd").age(12).sex(0).build());
studentList.add(Student.builder().name("ee").age(12).sex(1).build());
studentList.add(Student.builder().name("ff").age(10).sex(0).build());
studentList.add(Student.builder().name("gg").age(11).sex(1).build());
studentList.add(Student.builder().name("hh").age(13).sex(1).build());
studentList.add(Student.builder().name("ii").age(12).sex(0).build());
studentList.add(Student.builder().name("jj").age(11).sex(0).build());
// 计算年龄最大的学生
Student ageMax = studentList.stream().max(Comparator.comparing(Student::getAge)).get();
System.out.println("年龄最大的学生为:" + ageMax.getName() + " 年龄为:" + ageMax.getAge());
// 计算年龄最小的学生
Student ageMin = studentList.stream().min(Comparator.comparing(Student::getAge)).get();
System.out.println("年龄最小的学生为:" + ageMin.getName() + " 年龄为:" + ageMax.getAge());
// 年龄大于10岁的学生的数量
long gt10 = studentList.stream().filter(s -> s.getAge() > 10).count();
System.out.println("年龄大于10岁的学生的数量:" + gt10);
// 每一个年龄段的学生的数量
Map<Integer, Long> collect = studentList.stream().collect(Collectors.groupingBy(Student::getAge, Collectors.counting()));
System.out.println("每个年龄段的学生的数量:" + collect);
// 将学生按照性别分组
Map<Integer, List<Student>> collect1 = studentList.stream().collect(Collectors.groupingBy(Student::getSex, Collectors.toList()));
System.out.println("将学生按照性别分组" + collect1);
// 将学生按照性别分组后 只获取学生的姓名
Map<Integer, Set<String>> collect2 = studentList.stream().collect(Collectors.groupingBy(Student::getSex, Collectors.mapping(Student::getName, Collectors.toSet())));
System.out.println("将学生按照性别分组后,只取学生的姓名:" + collect2);
// 对所有的学生年龄+1
List<Student> collect3 = studentList.stream().map(s -> {
s.setAge(s.getAge() + 1);
return s;
}).collect(Collectors.toList());
System.out.println("将所有学生年龄+1:" + collect3);
// 将学生按照年龄进行排序
List<Student> sorted = studentList.stream().sorted(Comparator.comparing(Student::getAge)).collect(Collectors.toList());
System.out.println("将学生按照年龄进行排序:" + sorted);
// reduce 操作 将集合中的元素求和
Optional<Integer> total = Stream.of(1, 2, 3, 4, 5).reduce((x, y) -> x + y);
Integer total2 = Stream.of(1, 2, 3, 4, 5).reduce(0, (x, y) -> x + y);
// 将 List 转为 Map
List<Integer> collect4 = Stream.of(1, 2, 3, 4).collect(Collectors.toList());
Map<Integer, Integer> collect5 = collect4.stream().collect(Collectors.toMap(x -> x, x -> x + 1, (x, y) -> y));
System.out.println("list 转 Map:" + collect5);
Collectors 类的静态工厂方法。
工厂方法 | 返回类型 | 作用 |
---|---|---|
toList | List |
把流中所有项目收集到一个 List |
toSet | Set |
把流中所有项目收集到一个 Set,删除重复项 |
toCollection | Collection |
把流中所有项目收集到给定的供应源创建的集合menuStream.collect(toCollection(), ArrayList::new) |
counting | Long | 计算流中元素的个数 |
sumInt | Integer | 对流中项目的一个整数属性求和 |
averagingInt | Double | 计算流中项目 Integer 属性的平均值 |
summarizingInt | IntSummaryStatistics | 收集关于流中项目 Integer 属性的统计值,例如最大、最小、 总和与平均值 |
joining | String | 连接对流中每个项目调用 toString 方法所生成的字符串collect(joining(", ")) |
maxBy | Optional |
一个包裹了流中按照给定比较器选出的最大元素的 Optional, 或如果流为空则为 Optional.empty() |
minBy | Optional |
一个包裹了流中按照给定比较器选出的最小元素的 Optional, 或如果流为空则为 Optional.empty() |
reducing | 归约操作产生的类型 | 从一个作为累加器的初始值开始,利用 BinaryOperator 与流 中的元素逐个结合,从而将流归约为单个值累加int totalCalories = menuStream.collect(reducing(0, Dish::getCalories, Integer::sum)); |
collectingAndThen | 转换函数返回的类型 | 包裹另一个收集器,对其结果应用转换函数int howManyDishes = menuStream.collect(collectingAndThen(toList(), List::size)) |
groupingBy | Map<K, List |
根据项目的一个属性的值对流中的项目作问组,并将属性值作 为结果 Map 的键 |
partitioningBy | Map<Boolean,List |
根据对流中每个项目应用谓词的结果来对项目进行分区 |
Option 对象
Optional<Integer> optional = Optional.of(1); // 不允许值为null
Optional<Integer> optional1 = Optional.ofNullable(null); // 允许值为null
// 判断值是否存在 true 存在
if (optional1.isPresent()) {
// 获取存储的值,如果不存在,直接进行获取的话就报错
optional1.get();
}
optional.ifPresent(i -> System.out.println("存在 " + i));
optional1.orElse(100);
数据并行化
并发是两个任务共享时间段,并行是两个任务同一时间发生
parallelStream()
影响并行处理性能的因素 数据大小,元数据结构,装箱,可用cpu数量,处理每个元素的时间
影响运行性能测试的一些原因
- 热身效应:代码在运行100次和运行1分钟时的测量效果不一样,JIT的编译问题。
- 无用代码消除:DEC是一个编译器优化技术,目的是消除无用代码。
- 垃圾收集:在进行垃圾回收时,也会影响性能测试。
- 时钟:在使用
System.currentTimeMillis()
时,会有几毫秒的误差。
问题
关于接口的默认方法
接口A的默认方法为Stirng,接口B的默认方法为String,类C实现了A,B两个接口,此时需要实现String方法,否则就会出现钻石继承的问题。如果AB两个接口内的默认方法不冲突,则C不需要重新实现默认方法。