java8新特性
函数式接口
只有一个方法的接口(或者说只有一个方法需要实现的接口,java8支持接口有默认实现)
//常见的函数式接口
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
简单使用:
package fun;
import java.util.function.Function;
/**
* study01
*
* @author : xgj
* @description : de
* @date : 2020-09-21 15:09
**/
public class Main {
public static void main(String[] args) {
Function<String, Integer> function = new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.valueOf(s);
}
};
System.out.println(function.apply("123"));
}
}
精简为Lambda表达式
package fun;
import java.util.function.Function;
/**
* study01
*
* @author : xgj
* @description : de
* @date : 2020-09-21 15:09
**/
public class Main {
public static void main(String[] args) {
Function<String, Integer> function =(s)->{return Integer.valueOf(s);};
System.out.println(function.apply("123"));
}
}
再精简为流式计算
package fun;
import java.util.function.Function;
/**
* study01
*
* @author : xgj
* @description : de
* @date : 2020-09-21 15:09
**/
public class Main {
public static void main(String[] args) {
Function<String, Integer> function = Integer::valueOf;
System.out.println(function.apply("123"));
}
}
主要的函数式接口:
- Function<T,R> 传入一个参数,存在一个返回值。
- Predicate< T > 传入一个参数,返回一个布尔值。
- Consumer< T > 传入一个参数,没有返回值。
- Supplier< R > 没有参数,有一个返回值。
Lambda表达式
Lambda表达式式jdk8的新特性,支持将函数作为参数传递。使用 Lambda 表达式可以使代码变的更加简洁紧凑。但是其实牺牲了部分效率吧,就是需要进行类型推导。免去了使用匿名方法的麻烦(实际上没怎么变,只是将一些工作交给底层去做了),并且给予Java简单但是强大的函数化的编程能力。
public class Java8Tester {
public static void main(String args[]){
Java8Tester tester = new Java8Tester();
// 类型声明
MathOperation addition = (int a, int b) -> a + b;
// 不用类型声明
MathOperation subtraction = (a, b) -> a - b;
// 大括号中的返回语句
MathOperation multiplication = (int a, int b) -> { return a * b; };
// 没有大括号及返回语句
MathOperation division = (int a, int b) -> a / b;
System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
System.out.println("10 / 5 = " + tester.operate(10, 5, division));
// 不用括号
GreetingService greetService1 = message ->
System.out.println("Hello " + message);
// 用括号
GreetingService greetService2 = (message) ->
System.out.println("Hello " + message);
greetService1.sayMessage("Runoob");
greetService2.sayMessage("Google");
}
interface MathOperation {
int operation(int a, int b);
}
interface GreetingService {
void sayMessage(String message);
}
private int operate(int a, int b, MathOperation mathOperation){
return mathOperation.operation(a, b);
}
}
注意点,在匿名内部类中使用域外的局部变量时,需要变量被final修饰,但是Lambda编写代码的时候不用修饰(但是实际上还是需要修饰,只是人家的类型推导帮我们做了这一步)。
Stream流
- Stream是元素的集合,这点让Stream看起来用些类似Iterator;
- 可以支持顺序和并行的对原Stream进行汇聚的操作;
简单示例:
package fun;
import java.util.ArrayList;
import java.util.List;
/**
* study01
*
* @author : xgj
* @description : de
* @date : 2020-09-21 15:09
**/
public class Main {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i);
}
System.out.println(list.stream().filter((i) -> i > 5).count());
}
}
输出元素大于5的个数:
在此我们总结一下使用Stream的基本步骤:
- 创建Stream;
- 转换Stream,每次转换原有Stream对象不改变,返回一个新的Stream对象(可以有多次转换);
- 对Stream进行聚合(Reduce)操作,获取想要的结果;
创建方法:
Stream的静态方法
Collection集合类的默认实现方法.stream() (常用)
常用的转换方法:
- distinct: 对于Stream中包含的元素进行去重操作(去重逻辑依赖元素的equals方法),新生成的Stream中没有重复的元素;
package fun;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
/**
* study01
*
* @author : xgj
* @description : de
* @date : 2020-09-21 15:09
**/
public class Main {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i);
}
for (int i = 0; i < 10; i++) {
list.add(i);
}
Stream<Integer> distinct = list.stream().distinct();
distinct.forEach(System.out::println);
}
}
2. filter: 对于Stream中包含的元素使用给定的过滤函数进行过滤操作,新生成的Stream只包含符合条件的元素;
package fun;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
/**
* study01
*
* @author : xgj
* @description : de
* @date : 2020-09-21 15:09
**/
public class Main {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i);
}
for (int i = 0; i < 10; i++) {
list.add(i);
}
Stream<Integer> integerStream = list.stream().filter((i)-> i > 5);
integerStream.forEach(System.out::println);
}
}
- map: 对于Stream中包含的元素使用给定的转换函数进行转换操作,新生成的Stream只包含转换生成的元素。这个方法有三个对于原始类型的变种方法,分别是:mapToInt,mapToLong和mapToDouble。这三个方法也比较好理解,比如mapToInt就是把原始Stream转换成一个新的Stream,这个新生成的Stream中的元素都是int类型。之所以会有这样三个变种方法,可以免除自动装箱/拆箱的额外消耗;
package fun;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* study01
*
* @author : xgj
* @description : de
* @date : 2020-09-21 15:09
**/
public class Main {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i);
}
for (int i = 0; i < 10; i++) {
list.add(i);
}
Stream<Integer> intStream = list.stream().map((i)-> i+100);
intStream.forEach(System.out::println);
}
}
4. limit: 对一个Stream进行截断操作,获取其前N个元素,如果原Stream中包含的元素个数小于N,那就获取其所有的元素;
5. skip: 返回一个丢弃原Stream的前N个元素后剩下元素组成的新Stream,如果原Stream中包含的元素个数小于N,那么返回空Stream;
等等
链式编程:
将上述流处理运算组合在一起就是一种链式编程,还有像Stringbuffer的append函数。