一、 函数式接口
1、函数式接口主要指只包含一个抽象方法的接口,如:java.lang.Runnable、java.util.Comparator接口等。Java8提供@FunctionalInterface注解来定义函数式接口,若定义的接口不符合函数式的规范便会报错。
2、Java8中增加了java.util.function包,该包包含了常用的函数式接口,具体如下:
接口名称 | 方法声明 |
---|---|
Consumer | void accept(T t) 根据指定的参数执行操作 |
Supplier | T get() 得到一个返回值 |
Function<T,R> | R apply(T t) 根据指定的参数执行操作并返回 |
Predicate | boolean test(T t) 判断指定的参数是否满足条件 |
案例:匿名内部类实现函数式接口
import java.util.Comparator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
public class FunctionalInterfaceTest {
public static void main(String[] args) {
// 1.匿名内部类的语法格式: 父类/接口类型 引用变量名 = new 父类/接口类型(){ 方法的重写 };
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("我是既没有参数又没有返回值的方法!");
}
};
runnable.run(); // 我是既没有参数又没有返回值的方法!
System.out.println("--------------------------");
Consumer consumer = new Consumer() {
@Override
public void accept(Object o) {
System.out.println(o + "有参但没有返回值的方法就是我!");
}
};
consumer.accept("友情提示:"); // 友情提示:有参但没有返回值的方法就是我!
System.out.println("--------------------------");
Supplier supplier = new Supplier() {
@Override
public Object get() {
return "无参有返回值!";
}
};
System.out.println(supplier.get()); // 无参有返回值
System.out.println("--------------------------");
Function function = new Function() {
@Override
public Object apply(Object o) {
return o;
}
};
System.out.println(function.apply("有参有返回值的方法")); // 有参有返回值的方法
System.out.println("--------------------------");
Comparator comparator = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return 0;
}
};
System.out.println(comparator.compare(10, 20)); // 0
System.out.println("--------------------------");
Predicate predicate = new Predicate() {
@Override
public boolean test(Object o) {
return false;
}
};
System.out.println(predicate.test("hello")); // false
}
}
二、 Lambda表达式
1、Lambda 表达式是实例化函数式接口的重要方式,使用 Lambda 表达式可以使代码变的更加简洁紧凑。
2、lambda表达式:参数列表、箭头符号->和方法体组成,而方法体中可以是表达式,也可以是语句块。
3、语法格式:(参数列表) -> { 方法体; } - 其中()、参数类型、{} 以及return关键字 可以省略。
案例:使用lambda表达式实现函数式接口对象的创建: (参数列表)->{方法体;}
public class FunctionalInterfaceTest {
public static void main(String[] args) {
//Runnable runnable1 = () -> { System.out.println("我是既没有参数又没有返回值的方法!"); };
//方法体中若是只有一条语句,大括号可以省略
Runnable runnable1 = () -> System.out.println("我是既没有参数又没有返回值的方法!");
runnable1.run();
//Consumer consumer1 = (Object o) -> {System.out.println(o + "有参但没有返回值的方法就是我!");};
//Consumer consumer1 = (o) -> System.out.println(o + "有参但没有返回值的方法就是我!");
// 省略了()、参数类型、{}, 自动类型推断
Consumer consumer1 = o -> System.out.println(o + "有参但没有返回值的方法就是我!");
consumer1.accept("友情提示:");
//Supplier supplier1 = () -> {return "无参有返回值!";};
//省略了return关键字
Supplier supplier1 = () -> "无参有返回值!";
System.out.println(supplier1.get());
System.out.println("-------------------------");
// return 和 {} 都可以省略
Function function1 = o -> o;
System.out.println(function1.apply("有参有返回值的方法"));
System.out.println("--------------");
Comparator comparator1 = (o1, o2) -> 0;
System.out.println(comparator1.compare(10, 20));
System.out.println("----------------------");
Predicate predicate1 = o -> false;
System.out.println(predicate1.test("hello"));
}
}
三、方法引用
(一)、相关概念
1、方法引用主要指通过方法的名字来指向一个方法而不需要为方法引用提供方法体,该方法的调用交给函数式接口执行。
2、方法引用使用一对冒号 :: 将类或对象与方法名进行连接,通常使用方式如下:
- 对象的非静态方法引用 ObjectName :: MethodName
- 类的静态方法引用 ClassName :: StaticMethodName
- 类的非静态方法引用 ClassName :: MethodName
- 构造器的引用 ClassName :: new
- 数组的引用 TypeName[] :: new
3、方法引用是在特定场景下lambda表达式的一种简化表示,可以进一步简化代码的编写使代码更加紧凑简洁,从而减少冗余代码。
(二)、对象的非静态方法引用
public class MethodReferenceTest {
public static void main(String[] args) {
System.out.println("----------对象的非静态方法引用------");
System.out.println("----------无参数,无返回值---------");
// 1.需求:使用匿名内部类的方式通过函数式接口Runnable中的方法实现对Person类中show方法的调用
Person person = new Person("zhangfei", 30);
//方式一:用匿名内部类的函数式接口调用
Runnable runnable = new Runnable() {
@Override
public void run() {
person.show();
}
};
runnable.run(); // 没事出来秀一下哦
System.out.println("方式二:使用lambda表达式的方式实现");
Runnable runnable1 = () -> person.show();
runnable1.run(); // 没事出来秀一下哦
System.out.println("方式三:使用方法引用的方式实现");
Runnable runnable2 = person::show;
runnable2.run();
System.out.println("----------有参数,无返回值---------");
// 4.方式一:使用匿名内部类的方式通过函数式接口Consumer中的方法来实现Person类中setName方法的调用
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
person.setName(s);
}
};
consumer.accept("guanyu");
System.out.println("person = " + person); // guanyu 30
System.out.println("方式二:使用lambda表达式的方式实现");
Consumer<String> consumer1 = s -> person.setName(s);
consumer1.accept("liubei");
System.out.println("person = " + person); // liubei 30
System.out.println("方式三:使用方法引用的方式实现");
Consumer<String> consumer2 = person::setName;
consumer2.accept("zhangfei");
System.out.println("person = " + person); // zhangfei 30
System.out.println("----------无参数,有返回值---------");
// 7.方式一:使用匿名内部类的方式通过函数式接口Supplier中的方法来实现Person类中getName方法的调用
Supplier<String> supplier = new Supplier<String>() {
@Override
public String get() {
return person.getName();
}
};
System.out.println(supplier.get()); // zhangfei
System.out.println("方式二:使用lambda表达式的方式实现");
Supplier<String> supplier1 = () -> person.getName();
System.out.println(supplier1.get()); // zhangfei
System.out.println("方式三:使用方法引用的方式实现");
Supplier<String> supplier2 = person::getName;
System.out.println(supplier2.get()); // zhangfei
}
}
(三)、类的静态方法引用
public class MethodReferenceTest {
public static void main(String[] args) {
System.out.println("----------类的静态方法引用---------");
// 方式一:使用匿名内部类的方式通过函数式接口Function中的方法实现Integer类中parseInt方法的调用
Function<String, Integer> function = new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.parseInt(s);
}
};
System.out.println(function.apply("12345")); // 12345
// 方式二:lamda表达式
Function<String, Integer> function1 = s -> Integer.parseInt(s);
System.out.println(function1.apply("12345")); // 12345
// 方式三:方法的引用
Function<String, Integer> function2 = Integer::parseInt;
System.out.println(function2.apply("12345")); // 12345
System.out.println("------------------");
// 9.方式一:使用匿名内部类的方式通过函数式接口Comparator中的方法实现Integer类中compare方法的调用
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
System.out.println(comparator.compare(10, 20)); // -1
// 方式二:lamda表达式
Comparator<Integer> comparator1 = (o1, o2) -> Integer.compare(o1, o2);
System.out.println(comparator1.compare(10, 20)); // -1
// 方式三:方法的引用
Comparator<Integer> comparator2 = Integer::compare;
System.out.println(comparator2.compare(10, 20)); // -1
}
}
(四)、类的非静态方法引用
public class MethodReferenceTest {
public static void main(String[] args) {
System.out.println("------类名来调用非静态方法---------");
// 10.使用匿名内部类的方式通过类名来调用非静态方法
// 其中一个参数对象作为调用对象来调用方法时,可以使用上述方式 更抽象
Comparator<Integer> comparator3 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
};
System.out.println(comparator3.compare(10, 20)); // -1
// 方式二:lamda表达式
Comparator<Integer> comparator4 = (o1, o2) -> o1.compareTo(o2);
System.out.println(comparator4.compare(10, 20)); // -1
// 方式三:方法的引用
Comparator<Integer> comparator5 = Integer::compareTo;
System.out.println(comparator5.compare(10, 20)); // -1
}
}
(五)、构造器的引用
public class MethodReferenceTest {
public static void main(String[] args) {
System.out.println("---------构造器的引用----------");
// 11.使用匿名内部类的方式通过Supplier函数式接口创建Person类型的对象并返回
Supplier<Person> supplier3 = new Supplier<Person>() {
@Override
public Person get() {
return new Person();
}
};
System.out.println(supplier3.get()); // null 0
// 方式二:lamda表达式
Supplier<Person> supplier4 = () -> new Person();
System.out.println(supplier4.get()); // null 0
// 方式三:方法的引用
Supplier<Person> supplier5 = Person::new;
System.out.println(supplier5.get()); // null 0
System.out.println("--有参方式创建Person类型的对象并返回--");
// 12.使用匿名内部类的方式通过BiFunction函数式接口采用
BiFunction<String, Integer, Person> biFunction = new BiFunction<String, Integer, Person>() {
@Override
public Person apply(String s, Integer integer) {
return new Person(s, integer);
}
};
System.out.println(biFunction.apply("zhangfei", 30)); // zhangfei 30
// 方式二:lamda表达式
BiFunction<String, Integer, Person> biFunction1 = (s, integer) -> new Person(s, integer);
System.out.println(biFunction1.apply("zhangfei", 30)); // zhangfei 30
// 方式三:方法的引用
BiFunction<String, Integer, Person> biFunction2 = Person::new;
System.out.println(biFunction2.apply("zhangfei", 30)); // zhangfei 30
}
}
(六)、数组的引用
import java.util.Arrays;
import java.util.function.Function;
public class MethodReferenceTest {
public static void main(String[] args) {
System.out.println("-------通过Function函数式接口创建指定数量的Person类型的对象数组并返回-------------");
// 13.方式一:使用匿名内部类的方式
Function<Integer, Person[]> function3 = new Function<Integer, Person[]>() {
@Override
public Person[] apply(Integer integer) {
return new Person[integer];
}
};
Person[] pArr = function3.apply(3);
System.out.println(Arrays.toString(pArr));
// 方式二:lamda表达式
Function<Integer, Person[]> function4 = integer -> new Person[integer];
System.out.println(Arrays.toString(function4.apply(4)));
// 方式三:方法的引用
Function<Integer, Person[]> function5 = Person[]::new;
System.out.println(Arrays.toString(function5.apply(5)));
}
}
四、Stream接口
(一)、基本概念
java.util.stream.Stream接口是对集合功能的增强,可以对集合元素进行复杂的查找、过滤、筛选等操作。
Stream接口借助于Lambda 表达式极大的提高编程效率和程序可读性,同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势。
(二)、使用步骤
- 创建Stream,通过一个数据源来获取一个流。
- 转换Stream,每次转换返回一个新的Stream对象。
- 对Stream进行聚合操作并产生结果。
(三)、创建方式
- 方式一:通过调用集合的默认方法来获取流,如:default Stream stream()
- 方式二:通过数组工具类中的静态方法来获取流,如:static IntStream stream(int[] array)
- 方式三:通过Stream接口的静态方法来获取流,如:static Stream of(T… values)
- 方式四:通过Stream接口的静态方法来获取流,static Stream generate(Supplier<? extends T> s)
(四)、中间操作
1、筛选与切片的常用方法如下:
方法声明 | 功能介绍 |
---|---|
Stream filter(Predicate<? super T> predicate) | 返回一个包含匹配元素的流 |
Stream distinct() | 返回不包含重复元素的流 |
Stream limit(long maxSize) | 返回不超过给定元素数量的流 |
Stream skip(long n) | 返回丢弃前n个元素后的流 |
2、映射的常用方法如下:
方法声明 | 功能介绍 |
---|---|
Stream map(Function<? super T,? extends R> mapper) | 返回每个处理过元素组成的流 |
Stream flatMap(Function<? super T,? extends Stream<? extends R>> mapper) | 返回每个被替换过元素组成的流,并将所有流合成一个流 |
3、排序的常用方法如下
方法声明 | 功能介绍 |
---|---|
Stream sorted() | 返回经过自然排序后元素组成的流 |
Stream sorted(Comparator<? super T> comparator) | 返回经过比较器排序后元素组的流 |
(五)、终止操作
1、匹配与查找的常用方法如下:
方法声明 | 功能介绍 |
---|---|
Optional findFirst() | 返回该流的第一个元素 |
boolean allMatch(Predicate<? super T> predicate) | 返回所有元素是否匹配 |
boolean noneMatch(Predicate<? super T> predicate) | 返回没有元素是否匹配 |
Optional max(Comparator<? super T> comparator) | 根据比较器返回最大元素 |
Optional min(Comparator<? super T> comparator) | 根据比较器返回最小元素 |
long count() 返回元素的个数 | |
void forEach(Consumer<? super T> action) | 对流中每个元素执行操作 |
2、规约的常用方法如下:
方法声明 | 功能介绍 |
---|---|
Optional reduce(BinaryOperator accumulator) | 返回结合后的元素值 |
3、规约的常用方法如下:
方法声明 | 功能介绍 |
---|---|
<R,A> R collect(Collector<? super T,A,R> collector) | 使用收集器对元素进行处理 |
案例:
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class ListPersonTest {
public static void main(String[] args) {
// 1.准备一个List集合并放入Person类型的对象后打印
List<Person> list = new LinkedList<>();
list.add(new Person("zhangfei", 30));
list.add(new Person("xiaoqiao", 17));
list.add(new Person("zhouyu", 20));
list.add(new Person("zhangfei", 30));
list.add(new Person("guanyu", 35));
list.add(new Person("liubei", 40));
for (Person tp: list) {
System.out.println(tp);
}
System.out.println("------------");
// 2.将List集合中所有成年人过滤出来并放入另外一个集合中打印
List<Person> list1 = new LinkedList<>();
for (Person tp : list) {
if (tp.getAge() >= 18) {
list1.add(tp);
}
}
for (Person tp : list1) {
System.out.println(tp);
}
System.out.println("----------------");
// 3.使用Stream接口实现上述功能 匿名内部类的方式
list.stream().filter(new Predicate<Person>() {
@Override
public boolean test(Person person) {
return person.getAge() >= 18;
}
}).forEach(new Consumer<Person>() {
@Override
public void accept(Person person) {
System.out.println(person);
}
});
System.out.println("------------------");
// 4.使用lambda表达式对上述代码进行优化
//list.stream().filter(person -> person.getAge() >= 18).forEach(person -> System.out.println(person));
list.stream().filter(person -> person.getAge() >= 18).forEach(System.out::println);
System.out.println("---------------------");
// 5.实现对集合中元素通过流跳过2个元素后再取3个元素后打印
list.stream().skip(2).limit(3).forEach(System.out::println);
System.out.println("---------------------");
// 6.实现集合中所有元素中的年龄获取出来并打印
list.stream().map(new Function<Person, Integer>() {
@Override
public Integer apply(Person person) {
return person.getAge();
}
}).forEach(System.out::println);
//list.stream().map(person -> person.getAge()).forEach(System.out::println);
list.stream().map(Person::getAge).forEach(System.out::println);
System.out.println("---------------");
// 7.实现集合中所有元素的自然排序并打印
list.stream().sorted().forEach(System.out::println);
System.out.println("--------------");
// 8.判断集合中是否没有元素的年龄是大于45岁的
boolean b1 = list.stream().noneMatch(new Predicate<Person>() {
@Override
public boolean test(Person person) {
return person.getAge() > 45;
}
});
System.out.println("b1 = " + b1); // true
b1 = list1.stream().noneMatch(person -> person.getAge() > 45);
System.out.println("b1 = " + b1); // true
System.out.println("------------------");
// 9.按照指定的比较器规则获取集合所有元素中的最大值
Optional<Person> max = list.stream().max(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
});
System.out.println("按照年龄排序后的最大值是:" + max);
max = list.stream().max((o1, o2) -> o1.getAge() - o2.getAge());
System.out.println("按照年龄排序后的最大值是:" + max);
System.out.println("-------------------------------------------------------");
// 10.实现将集合中所有元素的年龄映射出来并进行累加后打印
Optional<Integer> reduce = list.stream().map(Person::getAge).reduce(new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
return integer + integer2;
}
});
System.out.println("最终所有年龄的累加和是:" + reduce); // 172
//reduce = list.stream().map(Person::getAge).reduce(((integer, integer2) -> integer + integer2));
reduce = list.stream().map(Person::getAge).reduce((Integer::sum));
System.out.println("最终所有年龄的累加和是:" + reduce); // 172
System.out.println("--------------------");
// 11.实现将集合中所有元素的姓名映射出来并收集到集合中打印
list.stream().map(Person::getName).collect(Collectors.toList()).forEach(System.out::println);
}
}
五、Optional类
(一)、基本概念
java.util.Optional类可以理解为一个简单的容器,其值可能是null或者不是null,代表一个值存在或不存在。该类的引入很好的解决空指针异常,不用显式进行空值检测。
(二)、常用的方法
方法声明 | 功能介绍 |
---|---|
static Optional ofNullable(T value) | 根据参数指定数值来得到Optional类型的对象 |
Optional map(Function<? super T,? extends U> mapper) | 根据参数指定规则的结果来得到Optional类型的对象 |
T orElse(T other) | 若该值存在就返回,否则返回other的数值。 |
案例:
import java.util.Optional;
import java.util.function.Function;
public class OptionalTest {
public static void main(String[] args) {
//String str1 = "hello";
String str1 = null;
if (null != str1) {
System.out.println("字符串的长度是:" + str1.length()); // 5 空指针异常
} else {
System.out.println("字符串为空,因此长度为0!");
}
System.out.println("--------------------");
// Java8中使用Optional类实现空值的处理
// 1.将数据str1装到Optional对象代表的容器中
Optional<String> optional = Optional.ofNullable(str1);
// 2.建立映射关系 使用字符串的长度与字符串建立映射关系
/*Optional<Integer> integer = optional.map(new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return s.length();
}
});*/
//Optional<Integer> integer = optional.map(s -> s.length());
Optional<Integer> integer = optional.map(String::length);
// 3.若字符串为空则打印0,否则打印字符串的数值
System.out.println("integer = " + integer); // Optional.empty
System.out.println(integer.orElse(0)); // 0
}
}