1-先说函数式接口
1.8开始新定义了一个注解FunctionalInterface(翻译过来就是:函数式接口)
* Conceptually, a functional interface has exactly one abstract method.(摘自jdk源码说明)
接口有且仅有一个抽象的方法,可以加上注解FunctionalInterface,比如我们经常使用的Runnable接口;
接口现在还可以拥有默认方法(即在类没有对方法进行实现时,其主体为方法提供默认实现的方法)。
哪怕有很多默认方法,只要接口只定义了一个抽象方法,它就仍然是一个函数式接口。
当然我们也可以自己定义函数式接口;只需要写一个接口,接口里面一个抽象方法(接口的方法默认都是抽象的),就可以在接口上加上@FunctionalInterface,标记为函数式接口...
2-Lambda表达式的语法
Lambda的基本语法是
(parameters) -> expression
或(请注意语句的花括号)
(parameters) -> { statements; }
Lambda表达式在Java语言中引入了 “->” 操作符, “->” 操作符被称为Lambda表达式的操作符或者箭头操作符,它将Lambda表达式分为两部分:
左侧部分指定了Lambda表达式需要的所有参数。
Lambda表达式本质上是对接口的实现,Lambda表达式的参数列表本质上对应着接口中方法的参数列表。
右侧部分指定了Lambda体,即Lambda表达式要执行的功能。
Lambda体本质上就是接口方法具体实现的功能。
Lambda只需要一个参数时,参数的小括号可以省略;
当Lambda体只有一条语句时,return和大括号可以省略;
Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器能够通过上下文推断出数据类型,这就是“类型推断”。
3-Java8核心的函数式接口
3.1-Consumer消费型接口
Consumer接口是消费性接口,接受一个参数,无返回值。
public static void testConsumer() {
testConsumerChild("abc", x-> System.out.println(x));
}
public static void testConsumerChild(String str,Consumer<String> consumer){
consumer.accept(str);
};
3.2-Supplier供给型接口
Supplier接口是供给型接口,有返回值
public static void testSupplier() {
List<String> list = testSupplierChild(3, () -> UUID.randomUUID().toString().replace("-", ""));
//遍历集合
list.stream().forEach(System.out::println);
}
public static List<String> testSupplierChild(int num, Supplier<String> supplier){
List<String> list = new ArrayList<>();
for(int i = 0; i < num; i++){
list.add(supplier.get());
}
return list;
}
3.3-Function<T, R>函数式接口
Function接口是函数型接口,一个入参,一个出参,有返回值
public static void testFunction() {
Integer function = testFunctionChild("abc", aaa -> aaa.length());
System.out.println(function);
}
public static Integer testFunctionChild(String str, Function<String, Integer> function) {
return function.apply(str);
}
3.4-Predicate断言型接口
Predicate接口是断言型接口,接受一个参数,返回值类型为boolean
public static void testPredicate() {
List<String> list = Arrays.asList("abc", "abcdef", "abcdjkl", "ycmy", "abcdjklmn");
List<String> strList = testPredicateChild(list, s -> s.length() >= 5);
strList.stream().forEach(System.out::println);
}
public static List<String> testPredicateChild(List<String> list, Predicate<String> predicate) {
List<String> strList = new ArrayList<>();
list.stream().forEach(aaa->{
if(predicate.test(aaa)){
strList.add(aaa);
}
});
return strList;
}
将 List 类型抽象化
public static <T> List<T> filter(List<T> list, Predicate<T> p){
List<T> result = new ArrayList<>();
for(T e: list){
if(p.test(e)){
result.add(e);
}
}
return result;
}
4-方法引用与构造器引用
4.1-方法引用
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!这里需要注意的是:实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!
如果一个Lambda代表的只是“直接调用这个方法”,那最好还是用名称来调用它,而不是去描述如何调用它。
方法引用就是操作符“::”将方法名和对象或类的名字分隔开来。
对象::实例方法
类::静态方法
类::实例方法
比如:
(x) -> System.out.println(x); 等价于:System.out::println
(str, i) -> str.substring(i) 等价于: String::substring
() -> Thread.currentThread().dumpStack() 等价于: Thread.currentThread()::dumpStack
4.2-构造器引用
与函数式接口相结合,自动与函数式接口中方法兼容。可以把构造器引用赋值给定义的方法,与构造器参数列表要与接口中抽象方法的参数列表一致!
ClassName::new
Supplier<Apple> c1 = Apple::new;等价于Supplier<Apple> c1 = () -> new Apple();
Function<Integer, Apple> c2 = Apple::new;
等价于
Function<Integer, Apple> c2 = (weight) -> new Apple(weight);
5-Stream
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
注意: Stream 自己不会存储元素。 Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
创建流(数据源) ---->中间操作 ---->终止操作(终端操作)
5.1-中间操作之筛选与切片
filter,distinct,limit,skip操作
private static void test001() {
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
.filter(i -> i % 2 == 0) //获取偶数
.distinct() //去重复
.forEach(System.out::println); //遍历
}
流支持limit(n)方法,该方法会返回一个不超过给定长度的流。所需的长度作为参数传递给limit。
流还支持skip(n)方法,返回一个扔掉了前n个元素的流。如果流中元素不足n个,则返回一个空流。请注意, limit(n)和skip(n)是互补的!
private static void test002() {
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4,6,8,10);
numbers.stream()
.filter(i -> i % 2 == 0) //获取偶数 2-2-4-6-8-10
.distinct() //去重复 2-4-6-8-10
.skip(2) //6-8-10
.limit(2) //6-8
.forEach(System.out::println); //遍历 6-8
}
5.2-映射map和flatMap
private static void test003() {
List<String> words = Arrays.asList("Java8", "Lambdas", "In", "Action");
words.stream()
.map(String::length)
.collect(Collectors.toList())
.forEach(System.out::println);
}
如果你要找出每道菜的名称有多长,怎么做?你可以像下面这样,再链接上一个map:
需求:
对于一张单词 表 , 如 何 返 回 一 张 列 表 , 列 出 里 面 各 不 相 同 的 字 符 呢 ? 例 如 , 给 定 单 词 列 表["Hello","World"],你想要返回列表["H","e","l", "o","W","r","d"]。
private static void test005() {
String[] arrayOfWords = {"Hello", "World"};
Stream<String> words = Arrays.stream(arrayOfWords);
words.map(word -> word.split(""))
.flatMap(Arrays::stream)
.distinct()
.collect(Collectors.toList())
.forEach(System.out::println);
}
使用flatMap方法的效果是,各个数组并不是分别映射成一个流,而是映射成流的内容。
给定两个数字列表,如何返回所有的数对呢?例如,给定列表[1, 2, 3]和列表[3, 4],应该返回[(1, 3), (1, 4), (2, 3), (2, 4), (3, 3), (3, 4)]。为简单起见,你可以用有两个元素的数组来代表数对。
private static void test006() {
List<Integer> numbers1 = Arrays.asList(1, 2, 3);
List<Integer> numbers2 = Arrays.asList(3, 4);
List<int[]> pairs = numbers1.stream()
.flatMap(i -> numbers2.stream()
.map(j -> new int[]{i, j})
)
.collect(Collectors.toList());
pairs.forEach( abc ->{
System.out.println(Arrays.toString(abc));
});
}
如何扩展前一个例子,只返回总和能被3整除的数对呢?例如(2, 4)和(3, 3)是可以的
private static void test007() {
List<Integer> numbers1 = Arrays.asList(1, 2, 3);
List<Integer> numbers2 = Arrays.asList(3, 4);
List<int[]> pairs = numbers1.stream()
.flatMap(i -> numbers2.stream().filter(j ->(i+j) %3==0)
.map(j -> new int[]{i, j})
)
.collect(Collectors.toList());
pairs.forEach( abc ->{
System.out.println(Arrays.toString(abc));
});
}
5.3-查找和匹配
Stream API通过allMatch、 anyMatch、 noneMatch、 findFirst和findAny方法提供了这样的工具。
通过单词就知道对应的含义,demo省略...
private static void test008() {
List<String> words = Arrays.asList("Java8", "Lambdas", "In", "Action");
Optional<String> optional = words.stream()
.filter(abc -> abc.length() > 30)
.findAny();
// System.out.println(optional.orElse("580"));//返回580
System.out.println(optional.isPresent()?optional.get():"590");
}
5.4-归约
Stream API通过reduce方法提供了这样的工具。
private static void test009() {
List<Integer> nums1 = Arrays.asList(1, 2, 3, 4);
Integer reduce1 = nums1.stream().reduce(0, (a, b) -> a + b);
System.out.println(reduce1);
List<Integer> nums2 = Arrays.asList(1, 2, 3, 4);
Integer reduce2 = nums2.stream().reduce(1, (a, b) -> a*b);
System.out.println(reduce2);
List<Integer> nums3 = Arrays.asList(1, 2, 3, 4,5,8,12,6,9,7);
Optional<Integer> reduce3 = nums3.stream().reduce(Integer::max);
System.out.println(reduce3.orElse(-1));
Optional<Integer> reduce4 = nums3.stream().reduce(Integer::min);
System.out.println(reduce4.orElse(-1));
}
5.5-操作小结
5.6-其他
Collectors.summingInt;
Collectors.averagingInt
//平均值
double avgCalories =menu.stream().collect(Collectors.averagingInt(Dish::getCalories));
IntSummaryStatistics menuStatistics =menu.stream().collect(Collectors.summarizingInt(Dish::getCalories));
这个收集器会把所有这些信息收集到一个叫作IntSummaryStatistics的类里,它提供了方便的取值( getter)方法来访问结果。打印menuStatisticobject会得到以下输出:IntSummaryStatistics{count=9, sum=4300, min=120,average=477.777778, max=800}
同样,相应的summarizingLong和summarizingDouble工厂方法有相关的LongSummaryStatistics和DoubleSummaryStatistics类 型 , 适 用 于 收 集 的 属 性 是 原 始 类 型long或double的情况
groupingBy-分组
Map<Dish.Type, List<Dish>> dishesByType =menu.stream().collect(groupingBy(Dish::getType));
partitioningBy-分区
Map<Boolean, List<Dish>> partitionedMenu =
menu.stream().collect(partitioningBy(Dish::isVegetarian));