1.1 原因:公司现在代码大量使用用函数式编程;代码可读性高;简化多层嵌套;处理大数据下集合的效率高(并行流),java并发编程多线程。
1.2 函数式编程思想:面向对象要关注什么对象完成何事,函数编程思想类似于数学函数。关注的是对数据进行什么操作。
1.3 优点
1.4 对接口要求
2.1 概述:是jdk8中的语法糖,对匿名内部类的写法简化。是函数式编程思想的重要提现。不用关注是什么对象,更关注对数据(参数)进行了什么操作(方法体)。
2.2 原则:可推导可简化
2.3 基本格式:(参数列表)->{code}
public class FunctionDemo01 {
public static void main(String[] args) {
int i = calcNum((left, right) -> left + right);
System.out.println(i);
}
static int calcNum(IntBinaryOperator operator) {
int a=10;
int b=20;
return operator.applyAsInt(a,b);
}
}
public static void main(String[] args) {
/*int i = calcNum((left, right) -> left + right);
System.out.println(i);*/
String s = typeConver(new Function() {
@Override
public String apply(String s) {
return s + "zk";
}
});
String s = typeConver(s1 ->s1 + "zk");
System.out.println(s);
}
static R typeConver(Function function){
String str = "456";
return function.apply(str);
}
2.4 省略规则
3.1 概述:jdk8的Stream使用函数式编程模式,被用来对集合和数组进行链状流式操作。
List list = new ArrayList();
Integer[] arr = {0,0,1,2,5,3,4,5,6,7,8,8,9};
list = Arrays.asList(arr);
list.stream() //把集合转换成流
.distinct() //去重
.filter(integer -> integer <= 5) //筛选条件
.forEach(integer -> System.out.println(integer)); //遍历打印
3.2 常用操作
3.2.1 创建流
单列集合:集合对象.stream()
List list = new ArrayList();
list = Arrays.asList(arr);
list.stream();
数组:Arrays.stream(arr)或Stream.of(arr)创建
Integer[] arr = {0,0,1,2,5,3,4,5,6,7,8,8,9};
Stream stream = Arrays.stream(arr);
Stream stream2 = Stream.of(arr);
双列集合:转换为单列集合后在创建。
Map map = new HashMap<>();
map.put("a",1);
map.put("b",2);
map.put("c",3);
Stream> stream1 = map.entrySet().stream();
stream1.filter(new Predicate>() {
@Override
public boolean test(Map.Entry entry) {
return entry.getValue()>1;
}
}).forEach(new Consumer>() {
@Override
public void accept(Map.Entry entry) {
System.out.println(entry.getKey()+"==="+entry.getValue());
}
});
//replace lambda
stream1.filter(entry -> entry.getValue()>1).forEach(entry -> System.out.println(entry.getKey()+"==="+entry.getValue()));
3.2.2 中间操作:先去重,后比较效率高。
Integer[] arr = {0,0,1,2,5,3,4,5,6,7,8,8,9};
Stream stream2 = Stream.of(arr);
stream2. //参数
map(integer -> integer + 99)
.forEach(integer -> System.out.println(integer));
public int compareTo(T o){
return this.x - o.x //相减 则比较大小,升序降序调换位置
}
List list = new ArrayList();
Integer[] arr = {0,0,1,2,5,3,4,5,6,7,8,8,9};
list = Arrays.asList(arr);
list.stream() //把集合转换成流
//.distinct() //去重
//.filter(integer -> integer <= 5) //筛选条件
//.sorted()
.sorted((o1, o2) -> o2 - o1) //大小比较,升序降序
.forEach(integer -> System.out.println(integer));
}
list.stream().sorted(Comparator.comparing(new Function() {
@Override
public Integer apply(Integer integer) {
return integer;
}
}).reversed()).forEach(num-> System.out.println(num));
//升序可以识别匿名内部类参数类型,参数类型可以省略
list.stream().sorted(Comparator.comparing(integer -> integer)).forEach(num-> System.out.println(num));
//降序无法识别匿名内部类参数类型,方法体因参数类型无法确定而调用失效,参数类型不可以省略
list.stream().sorted(Comparator.comparing((Function) integer -> integer).reversed()).forEach(num-> System.out.println(num));
//内存分页操作
private static void test05(List list) {
int page = 0;
final int pageNum = 3;
int pageSize = (list.size() % pageNum) == 0? list.size()/pageNum:list.size()/pageNum +1;
for (int i=0; i < pageSize; i++) {
page = i*pageNum;
list.stream().skip(page).limit(pageNum).forEach(x-> System.out.print(x+","));
System.out.println();
}
}
List list = new ArrayList();
Integer[] arr = {0,0,1,2,5,3,4,5,6,7,8,8,9};
list = Arrays.asList(arr);
/*list.stream() //把集合转换成流
.distinct() //去重
.filter(integer -> integer <= 5) //筛选条件
.forEach(integer -> System.out.println(integer)); //遍历打印*/
//对流中元素里的每个list和数组再转换为Stream对象元素作为流元素,进行操作
list.stream().flatMap(new Function>() {
@Override
public Stream apply(Integer integer) {
List list1 = new ArrayList<>();
list1.add(String.valueOf(integer)+"qwe");
return list1.stream();
}
}).distinct().forEach(s -> System.out.println(s));
list.stream().flatMap(book->Arrays.stream(book.getCategory().split(","))).forEach()
"xxx,xxx"格式分割为数组,转换为流对象元素
List list = Arrays.asList("ab", "cd", "ef", "gh");
List list2 = Arrays.asList("ij", "kl", "mo", "pq");
//两个一维数组拼接成二维数组
List> collect = Stream.of(list, list2).collect(Collectors.toList());
collect.forEach(list1 -> System.out.println(list1));
//在用flatMap降维度
collect.stream().flatMap(new Function, Stream>() {
@Override
public Stream apply(List list) {
return list.stream();
}
}).forEach(s -> System.out.println(s));
collect.stream().flatMap((Function, Stream>) list12 -> list12.stream()).forEach(s -> System.out.println(s));
3.2.3 终结操作:用流则必须要有此操作,否则中间操作中的过程不会执行。
stream.distinct() //去重
.filter(integer -> {
System.out.println("test"); //不会执行
;return integer <= 5;}); //筛选条件
//.forEach(integer -> System.out.println(integer)); //遍历打印
List list = Arrays.asList("ab", "cd", "ef", "gh");
list.forEach(new Consumer() {
@Override
public void accept(String s) {
System.out.println("匿名内部类方式:"+s);
}
});
list.forEach(s -> System.out.println("lambda方式:"+s));
//使用java.util.stream.Collectors;
//工具类里面是静态方法
List collect = stream.collect(Collectors.toList())
//Collectors.toMap两个参数,分别指明key和value值的转换关系
List list = new ArrayList();
Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
list = Arrays.asList(arr);
Map
List list1 = Arrays.asList("3", "5", "6", "1", "9", "8");
//使用collcet将流中元素转换为字符串,用特殊符号分割
String str = list1.stream().collect(Collectors.joining(","));
System.out.println(str);
List list = Arrays.asList(3, 5, 2, 1, 3, 5, 2, 1, 3, 5, 2, 1);
//用collect将list转为set去重
list.stream().collect(Collectors.toSet()).forEach(x-> System.out.println(x));
//用collect将list转为LinkedList
list.stream().collect(Collectors.toCollection(new Supplier() {
@Override
public LinkedList get() {
return new LinkedList();
}
})).forEach(x-> System.out.println(x));
System.out.println("=========");
list.stream().collect(Collectors.toCollection(() -> new LinkedList())).forEach(x-> System.out.println(x));
System.out.println("=========");
list.stream().collect(Collectors.toCollection(LinkedList::new)).forEach(x-> System.out.println(x));
System.out.println("用collect将list转为TreeSet");
list.stream().collect(Collectors.toCollection(TreeSet::new)).forEach(x-> System.out.println(x));
查找与匹配:
anyMatch:是否有符合匹配的,返回返回boolean值
allMatch:是否都符合匹配条件,返回boolean值
noneMatch:都不符合匹配条件,返回boolean值
findAny:获取流中任意一元素,不保证获取的一定是流中第一个元素;从刷选结果中组装成Optional,不一定存在得判断
List list = new ArrayList();
Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
list = Arrays.asList(arr);
Optional any = list.stream().
distinct().
filter(num -> num > 8).findAny();
//有数据则输出,没有则不会执行方法体,不报空指针异常
any.ifPresent(num-> System.out.println(num));
reduce归并:对流中的数据按照自定义的计算方式计算出一个结果(缩减操作)。作用把stream的元素组合起来,可以传入一个初始值,按照计算方式依次拿流中元素和初始化值进行计算,计算后在和后面的元素计算
//reduce两个参数的重载形式
T result = identity; //初始值
for(T element:this stream)
result = accumulator.apply(result,element);//自定义方法
return result;
List list = new ArrayList();
Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
list = Arrays.asList(arr);
Integer results = list.stream().
distinct().
reduce(0, (result, ele) -> result + ele);
System.out.println(results);
//求最大值,max底层用了reduce方法
List list = new ArrayList();
Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
list = Arrays.asList(arr);
Integer results = list.stream().
distinct(). //最小值
reduce(Integer.MIN_VALUE, (result, ele) -> result < ele?ele:result);
System.out.println(results);
List list = Arrays.asList(3, 5, 2);
List list1 = Arrays.asList(2, 1, 3);
List list2 = Arrays.asList(1, 3, 5);
//同时拼接3个list
List[] lists = {list,list1,list2};
//设置空stream流中间变量,迭代计算好的结果
Stream streamTmp = Stream.builder().build();
for (List ls: lists) {
streamTmp = Stream.concat(streamTmp, ls.stream());
}
streamTmp.forEach(x-> System.out.println(x));
//Stream.concat()拼接两个list
Stream result1 = Stream.concat(list.stream(), list1.stream());
//Stream.concat(result1,list2.stream()).forEach(x-> System.out.println(x));
3.3 注意事项
4.1 概述:编写代码时出现空值针异常。要做非空判断。过多的判断会显得臃肿。所以在JDK8中引入Optional,可以避免空值异常
4.2 使用
list.notNull ==>
if (list != null) {
}
4.2.1 创建对象:Optional像包装类,可以把具体的数据封装到Optional对象内部(作为属性)。使用Optional封装好的方法操作封装进去的数据可以避免空指针异常。使用Optional的静态方法ofNullable把数据封装成一个Optional对象。
Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
Optional integers = Optional.ofNullable(arr);
//使用,里面是否有值
integers.ifPresent(integers1 -> System.out.println(integers1.length));
实际开发中数据是从数据库中获取的。mybatis3.5可以支持Optional可以把dao方法返回值定义为Optional,mybatis会自动把数据封装为Optional对象返回。将方法返回值设置为Optional对象。
如果确定封装数据不是空可以用Optional的静态方法of来把数据封装为Optional对象。
若方法返回值类型为Optional。而不确定返回值都不为null,这个时候需要把null封装为Optional返回。可以用Optional的静态方法empty来进行封装。
Optional
4.2.2 安全消费值:使用Optional的ifPresent来使用值,会做非空判断,非空时才使用代码
Optional any = list.stream().
distinct().
filter(num -> num > 8).findAny();
//有数据则输出,没有则不会执行方法体,不报空指针异常
any.ifPresent(num-> System.out.println(num));
4.2.3 获取值:获取值自定义处理可以用get方法获取,不推荐有空指针异常。
4.2.4 安全获取值
Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
Optional integers = Optional.ofNullable(arr);
Integer[] integers2 = integers.orElseGet(() -> new Integer[0]);
System.out.println(integers2.length);
=============================
Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
Optional integers = Optional.ofNullable(null);
Integer[] integers2 =
integers.orElseGet(() -> {
Integer[] integers3 = new Integer[2];
integers3[0] = 11;
integers3[1] = 22;
return integers3;
});
System.out.println(integers2.length);
List list = new ArrayList();
Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
Optional integers = Optional.ofNullable(null);
Integer[] integers1 = new Integer[0];
try {
integers1 = integers.orElseThrow(() -> new RuntimeException("数据为null!"));
} catch (Throwable throwable) {
throwable.printStackTrace();
}
System.out.println(integers1.length);
4.2.5 过滤:可以用filter方法数据对数据进行过滤。若经过判断从哪有数据变为无数据的Option对象。
Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
Optional integers = Optional.ofNullable(arr);
integers.filter(integers1 -> integers1.length>15).ifPresent(integers12 -> System.out.println(integers12));
String str = "abc";
Optional str1 = Optional.of(str);
str1.filter(strs->strs.matches("abc")).ifPresent(s -> System.out.println(s));
}
4.2.6 判断:Optional.isPresent()判断数据是否存在。提现不出Optional好处,推荐用ifPresent用法;
String str2 = "abc";
Optional str3 = Optional.of(str2).filter(strs -> strs.matches("abc"));
if ( str3.isPresent() ) {//判断过了
System.out.println(str3.get());/此操作安全
}
4.2.7 数据转换:利用Optional.map方法可以进行数据转换,转换后的数据也是被Optional包装好的,使用上安全。同stream流中的方法一致。
integers.map(integers13 -> integers13.toString() +"abv").ifPresent(st-> System.out.println(st));
5.1 概述:只有一个抽象方法的接口称为函数接口。jdk的函数式接口都加上了@FunctionalInterface注解进行标识。是否加上注解,只要接口中只有一个抽象方法,都是函数式接口。
5.2 常见的函数式接口:java.util.function包下
public interface Consumer{void accept(T t); } //按照抽象方法参数列表和返回值类型,可以对传入的参数进行使用
public interface Function
{R apply(T t)} //计算转换接口,将参数进行计算或转换并把结果返回 public interface Predicate{boolean test(T t);} //判断接口,对参数进行条件判断,把判断结果返回
public interface Supplier{T get();} //生产接口,在方法中创建对象并返回
5.3 常用的默认方法:单独的stream流不用,自定义函数式方法时会用到。
List list = new ArrayList();
Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
list = Arrays.asList(arr);
list.stream()
.distinct() //是Predicate对象的and方法
.filter(((Predicate) integer -> integer > 3).and(num -> num<8)).forEach(a-> System.out.println(a));
testOptional07(o -> (Integer)o%2==0, o -> (Integer)o>4);
List list = new ArrayList();
int[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
for (int i : arr) {
if(predicate1.and(predicate2).test(i)){
System.out.println("筛选结果:"+i);
}
}
testOptional08();
List list = new ArrayList();
Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
list = Arrays.asList(arr);
list.stream()
.distinct()
.filter(((Predicate) integer -> integer > 3).negate()).forEach(a-> System.out.println(a));
方法体只有一个方法的调用(包括构造方法),可以使用方法引用进一步简化代码
6.1 推荐方法
使用lambda时不需要考虑什么时候用方法应用,用那种方法应用,引用格式是什么、写完lambda方法体只有一行代码。且是方法的调用时用快捷键尝试能否转换为方法引用即可
6.2 基本格式:表达式引用方法
6.2.1 前提
6.2.2 类名或对象名::方法名
/*接口引用指向实现方法,然后在main方法中使用 类名(静态方法)/对象(成员方法)::方法名*/
public static void main(String[] args) {
//静态方法--Lambda表达式引用
MyFunctionInterface mf = (a, b) -> sumImp(a,b);
int sum = mf.sum(3, 6);
System.out.println(sum);
//静态方法--在其他类中调用本类方法的引用 类::方法名
MyFunctionInterface mf1 = Demo01::sumImp;
int sum1 = mf1.sum(9, 8);
System.out.println(sum1);
//new对象引用方法对象名::方法名
Demo01 demo01 = new Demo01();
MyFunctionInterface mf2 = demo01::subImp;
int sum2 = mf2.sum(9, 7);
System.out.println(sum2);
}
public static int sumImp(int a,int b){
return a+b;
}
public int subImp(int a,int b){
return a-b;
}
}
interface MyFunctionInterface{
int sum(int a,int b);
}
6.3 语法详解:
6.3.1 引用静态方法:引用类的静态方法:
类名::方法名
6.3.2 引用对象的实例方法
对象名::方法名
前提:重写方法时,方法体只有一行代码,还是调用了某个对象的成员方法,且要重写抽象方法中所有参数都按照顺序传入这个成员方法中,此时就可以引用对象的成员方法。
List list = new ArrayList();
Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
list = Arrays.asList(arr);
StringBuilder sb = new StringBuilder();
list.stream()
.distinct()
.map(integer -> integer+"").forEach(str -> sb.append(str));
System.out.println(sb.toString());
list.stream()
.distinct() //类静态方法 //对象实例方法
.map(String::valueOf).forEach(sb::append);
6.3.3 引用类的实例方法
类名::方法名
前提:重写方法时,方法体只有一行代码,还是调用了第一个参数的成员方法,且要重写抽象方法中所有参数都按照顺序传入这个成员方法中,此时就可以引用类的实例方法。
public class OptionalDemo02 {
public static void main(String[] args) {
String str = subUsing("lambda is easy", new Using() {
@Override
public String use(String str, int i, int endIndex) {
return str.substring(i, endIndex);
}
});
System.out.println(str);
}
public static String subUsing(String str,Using using){
int index = 7;
int endIndex = 12;
return using.use(str,index,endIndex);
}
}
interface Using {
String use(String str,int i,int length);
}
String str = subUsing("lambda is easy", String::substring);
System.out.println(str);
6.3.4 构造器引用:(接口实例指向类的构造函数)方法体中的一行代码时构造器的话可以使用构造器引用。
类名::new
前提:重写方法时,方法体只有一行代码,还是调用了某个类的构造方法,且要重写抽象方法中所有参数都按照顺序传入这个构造方法中,此时就可以引用类的实例方法。
private static void testConstructor() {
List list = new ArrayList();
Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
list = Arrays.asList(arr);
list.stream()
.distinct()
.map(String::valueOf)
.map(StringBuffer::new)
.forEach(str-> System.out.println(str.append("dnf")));
}
public class Demo02 {
public static void main(String[] args) {
AnimalInterface01 ai01 = () -> new Cat();
ai01.fatchAnimal();
ai01 = Cat::new;
ai01.fatchAnimal();
/*有参构造函数*/
AnimalInterface02 ai02 = Cat::new;
Cat cat = ai02.fatchAnimal("来福", 2);
System.out.println(cat.getName());
}
}
@FunctionalInterface
interface AnimalInterface01{
public Cat fatchAnimal();
}
@FunctionalInterface
interface AnimalInterface02{
public Cat fatchAnimal(String name, int size);
}
class Cat{
private String name;
private int size;
public Cat() {
System.out.println("无参构造方法!");
}
public Cat(String name, int size) {
System.out.println("含参构造方法!");
this.name = name;
this.size = size;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Cat cat = (Cat) o;
return size == cat.size &&
Objects.equals(name, cat.name);
}
@Override
public int hashCode() {
return Objects.hash(name, size);
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
", size=" + size +
'}';
}
}
7.1基本数据类型优化:Stream方法使用了泛型,参数和返回值是引用数据类型,在方法体中使用的是基本数据类型,中间经过自动拆装箱的过程,这个过程要消耗时间大数据量时,系统效率受损。
List list = new ArrayList();
Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
list = Arrays.asList(arr);
list.stream()
.distinct()
.mapToInt(value -> value) //此处做类型转换,后续都用基本数据类型
.map(operand -> operand+5).forEach(System.out::println);
7.2 并行流:前面的都是串行流
private static void testConstructor3() {
List list = new ArrayList();
Integer[] arr = {0, 0, 1, 2, 5, 3, 4, 5, 6, 7, 8, 8, 9};
list = Arrays.asList(arr);
list.stream()
.distinct()
.parallel() //开启多线程
.peek(x-> System.out.println(x+"::"+Thread.currentThread().getName())) //中间操作调试元素
.reduce((integer, integer2) -> integer+integer2)
.ifPresent(System.out::println);
}
list.parallelStream() //直接创建并行流
.distinct()
.peek(x-> System.out.println(x+"::"+Thread.currentThread().getName())) //中间操作调试元素
.reduce((integer, integer2) -> integer+integer2)
.ifPresent(System.out::println);