1、lambda表达式
//lambda完成集合遍历
list.forEach(value-> System.out.println(value+","));
//lambda改造匿名内部类使用
Runnable runnable1 = ()-> System.out.println("hello itheima lambda");
//lambda完成集合排序
Arrays.sort(language,(o1,o2)->o1.compareTo(o2));
查看内部类里面的内容,可以在lambda表达式执行之前,添加
System.setProperty("jdk.internal.lambda.dumpProxyClasses", "D://");
这个方法会将运行时生成的内部类class文件进行输出。当该文件生成后,可以通过 javap -c -p class文件名 查看文件中的内容
Lambda使用外部定义变量
public void demo(){
int port = 8086;
Runnable runnable = ()-> System.out.println(port);
}
在这段代码中,在Lambda表达式内部引用了外部变量。但是当在Lambda方法体内使用外部变量时,其必须声明为final。
上述代码虽然没有显示的声明,但是在Java8它自动的会对需要为final的变量进行转换。
2、函数式接口
有且只有一个抽象方法的普通接口,如果声明多个抽象方法则会报错。
但是默认方法和静态方法在此接口中可以定义多个。
在Java8的类库设计中,已经引入了几个函数式接口:
1、Predicate数据判断
public static List<Student> filter(List<Student> studentList, Predicate<Student> predicate){
List<Student> list = new ArrayList<>();
studentList.forEach(s->{
if (predicate.test(s)){
list.add(s);
}
});
return list;
}
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(new Student(1,"张三","M"));
students.add(new Student(2,"李四","M"));
students.add(new Student(3,"王五","F"));
List<Student> result = filter(students, (s) -> s.getSex().equals("F"));
System.out.println(result.toString());
}
2、Consumer获取数据,方法没返回。
public class MyConsumerDemo {
public static void foreach(List<String> list, Consumer<String> consumer){
list.forEach(v->consumer.accept(v));
}
public static void main(String[] args) {
List<String> arrays = new ArrayList<>();
arrays.add("java");
foreach(arrays,(s)-> System.out.println(s+","));
}
}
3、Function数据类型转换
public class MyFunctionDemo {
public static Integer convert(String value, Function<String,Integer> function){
return function.apply(value);
}
public static void main(String[] args) {
String value ="666";
Integer result = convert(value, (s) -> Integer.parseInt(value) + 222);
System.out.println(result);
}
}
4、Supplier获取数据,并且方法有返回。
public static Integer getMin(Supplier<Integer> supplier){
return supplier.get();
}
public static void main(String[] args) {
int[] arr = {100,20,50,30,99,101,-50};
Integer result = getMin(() -> {
int min = arr[0];
for (int i : arr) {
if (i < min) {
min = i;
}
}
return min;
});
System.out.println(result);
}
3、Stream常用函数
常用函数
Stream<Object> objectStream = Stream.of("1", 2, true, new St());
Integer[] numbers = new Integer[]{1,2,3,4,5,6};
Stream<Integer> stream = Arrays.stream(numbers);
String数组转Int数组
String[] orgArray =a;
int[] orgIds = Arrays.stream(a).mapToInt(Integer::valueOf).toArray();
List<String> result = list.stream() .filter(s -> s.getAge() < 20)
.sorted(Comparator.comparing(Student::getAge)).map(Student::getName)
.collect(Collectors.toList()).distinct().limit(3).skip(2).findFirst();
distinct:去空对象集合,需要重写hashcode、equals方法。
findAny用于获取流中随机的某一个元素,findAny对于同一数据源的多次操作会返回不同的结果。但是,
我们现在的操作是串行的,所以在数据较少的情况下,一般会返回第一个结果,但是如果在并行的情况
下,那就不能确保返回的是第一个了
.map(Student::getName).map(String::length) //也可以继续向下获取每一个名称的长度
集合元素拼接
拼接符可有可无
String collect = studentList.stream().map(Student::getName).collect(Collectors.joining(","));
合并流
Stream.concat(s1,s2).map(name->new Person(name)).forEach(p-> System.out.println(p));
对集合排序
students.sort((s1,s2)->s1.getId().compareTo(s2.getId()));
Comparator<Student> comparator = Comparator.comparing((Student s) -> s.getId());
list.sort(Comparator.comparing((s)->s.getId()));
list.sort(Comparator.comparing(Student::getId));
全部匹配
list.stream().allMatch(Student::getIsPass)
至少有一个匹配
list.stream().anyMatch(s->s.getAge()<20)
集合总数统计
Long collect =list.stream().collect(Collectors.counting());
long count = studentList.stream().count();
根据年龄进行分组,获取并汇总人数
Map<Integer, Long> collect = studentList.stream().collect(Collectors.groupingBy(Student::getAge, Collectors.counting()));
分组
Map<String, List<Student>> map = list.stream().collect(Collectors.groupingBy(Student::getSex));
根据年龄和是否通过分组
Map<Integer, Map<String, List<Student>>> collect =studentList.stream().collect(Collectors.group
ingBy(Student::getAge,Collectors.groupingBy(student -> {
if (student.getIsPass()) {
return "pass";
} else {
return "not pass";
}
})));
根据年龄与是否及格进行分组,并汇总人数
Map<Integer, Map<Boolean, Long>> collect = studentList.stream().collect(Collectors.groupingBy(Student::getAge, Collectors.groupingBy(Student::getIsPass, Collectors.counting())));
根据年龄与是否通过进行分组,并获取每组中分数最高的学生
Map<Integer, Map<Boolean, Student>> collect = studentList.stream().collect(
Collectors.groupingBy(Student::getAge,
Collectors.groupingBy(Student::getIsPass,
Collectors.collectingAndThen(
Collectors.maxBy(
Comparator.comparing(Student::getScore)
), Optional::get
)))
);
最大最小值获取
没优化前:
Optional<Student> optional=studentList.stream().collect(Collectors.maxBy(Comparator.comparing(Student::getAge)));
优化后:max-min
Optional<Student> min = studentList.stream().min(Comparator.comparing(Student::getAge));
if (min.isPresent()){
System.out.println(min.get());
}
集合中值相加
Integer collect = studentList.stream().collect(Collectors.summingInt(Student::getAge));
上边优化后:
int sum = studentList.stream().mapToInt(Student::getAge).sum();
int sum = numbers.parallelStream().mapToInt(i ->i).sum();
集合中平均值计算
Double collect1 = studentList.stream().collect(Collectors.averagingInt(Student::getAge));
这种方式虽然代码写多了一些,但是可以防止空值的出现:
OptionalDouble average = studentList.stream().mapToDouble(Student::getAge).average();
if (average.isPresent()){
System.out.println(average.getAsDouble());
}
集合函数计算(长度、总和、最大最小、平均值)
IntSummaryStatistics collect = studentList.stream().collect(Collectors.summarizingInt(Student::getAge));
long count = collect.getCount();
long sum = collect.getSum();
int max = collect.getMax();
int min = collect.getMin();
double average = collect.getAverage();
}
获取所有的文件列表信息和文件中的内容
Files.list(Paths.get("D:\\workspace")).forEach(path-> {
System.out.println(path);
//读取每一个文件中的内容信息
try {
Files.lines(path).forEach(content-> System.out.println(content));
} catch (IOException e) {
e.printStackTrace();
}
});
初始值是0,每次拿集合中的2个数相加
Integer reduce = numbers.stream().reduce(0, (a, b) -> a + b);
Integer reduce = numbers.stream().reduce(0, Integer::sum);
Optional<Integer> optional = numbers.stream().reduce(Integer::sum);
if (optional.isPresent()){
Integer integer = optional.get();
System.out.println(integer);
}
获取最大、最小值
Optional<Integer> maxOptional = numbers.stream().reduce(Integer::max);
Optional<Integer> maxOptional = numbers.stream().max(Integer::compareTo);
Optional<Integer> minOptional = numbers.stream().min(Integer::compareTo);
if (maxOptional.isPresent()){
System.out.println(maxOptional.get());
}
并行流介绍
stream():串行流
parallelStream():并行流,在核数多等业务情况下效率高
findAny串行流且数据较少时,获取的结果一般为流中第一个元素,但是当为并流行的时候,则会随机获取
List<Student> result = list.parallelStream().filter(Student::getIsPass).findAny().collect(Collectors.toList());
使用注意事项
对于并行流,一定不要陷入一个误区:并行一定比串行快。并行在不同的情况下它不一定是比串行快的。影响并行流性能主要存在5个因素:
1)数据大小:输入数据的大小,直接影响了并行处理的性能。因为在并行内部实现中涉及到了fork/join操作,它本身就存在性能上的开销。
因此只有当数据量很大,使用并行处理才有意义。
2)源数据结构:fork时会对源数据进行分割,数据源的特性直接影响了fork的性能。
ArrayList、数组或IntStream.range,可分解性最佳,因为他们都支持随机读取,因此可以被任意分割。
HashSet、TreeSet,可分解性一般,其虽然可被分解,但因为其内部数据结构,很难被平均分解。
LinkedList、Streams.iterate、BufferedReader.lines,可分解性极差,因为他们长度未知,无法确定在哪里进行分割。
3)装箱拆箱:尽量使用基本数据类型,避免装箱拆箱。
4)CPU核数:fork的产生数量是与可用CPU核数相关,可用的核数越多,获取的性能提升就会越大。
5)单元处理开销:花在流中每个元素的时间越长,并行操作带来的性能提升就会越明显。
性能测试
通过基本类型、对象类型和复杂对象来对普通for循环、串行流和并行流进行分别的性能测试
此处使用常见线上服务器配置。12核24线程,96G内存的配置。
数据量是1*10的6次方以上测试结果:
1、对于基本数据类型Stream串行的性能开销是普通for循环的两倍左右。同时Stream并行的性能比普通for循环和串行都要好。
2、当操作对象类型时,Stream串行的性能开销仍高于普通for循环一倍左右。同时Stream并行的性能比普通for循环和串行都要好。
3、当操作复杂对象时,普通for循环的性能开销会高于Stream串行,同时仍然是Stream并行性能最优
4、接口
类实现接口规则
普通方法必须实现,默认方法可以选择性重写,静态方法无法重写。
1、接口中可以定义多个默认方法,并且可以有方法体自己实现。调用接口中默认方法用对象.默认方法名
2、接口中可以定义多个静态方法,调用接口中静态方法用接口.静态方法名。
//抽象方法
String abstractMethod();
//默认方法
default String defaultMethod(){
return "execute default method";
}
//静态方法
static String staticMethod(){
return "execute static method";
}
5、方法引用
使用语法
类名或实例名::方法名
6、自定义收集器
/**
* 自定义收集器返回所有合格的学员
*/
public class MyCollector implements Collector<Student, List<Student>,List<Student>> {
@Override
public Supplier<List<Student>> supplier() {
return ArrayList::new;
}
@Override
public BiConsumer<List<Student>, Student> accumulator() {
return ((studentList, student) -> {
if (student.getIsPass()){
studentList.add(student);
}
});
}
@Override
public BinaryOperator<List<Student>> combiner() {
return null;
}
@Override
public Function<List<Student>, List<Student>> finisher() {
return Function.identity();
}
@Override
public Set<Characteristics> characteristics() {
return EnumSet.of(Characteristics.IDENTITY_FINISH,Characteristics.UNORDERED);
}
}
使用
public static void main(String[] args) {
List<Student> studentList = new ArrayList<>();
studentList.add(new Student(3,"王五","F",21,true));
studentList.add(new Student(1,"张三","M",19,true));
studentList.add(new Student(4,"赵六","F",20,false));
studentList.add(new Student(2,"李四","M",18,true));
List<Student> list = studentList.stream().collect(new MyCollector());
System.out.println(list);
}