以下是本人观看尚硅谷Java学习视频所做的笔记
Java 8 (又称为jdk 1.8) 是Java语言开发的一个主要版本。
Java 8是oracle公司于2014年3月发布,可以看成是自Java 5以来最具革命性的版本。Java 8为Java语言、编译器、类库、开发工具与JVM带来了大量新特性。
Java 8 特点:
并行流与串行流:
并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。相比较串行的流,并行的流可以很大程度上提高程序的执行效率。
Java 8中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API可以声明性地通过parallel() 与 sequential()在并行流与顺序流之间进行切换。
Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
举例:(o1, o2) -> Integer.compare(o1, o2);
本质:作为函数式接口的实例
格式:
->
:lambda操作符 或 箭头操作符->的左边
:lambda形参列表(其实就是接口中的抽象方法的形参列表)->的右边
:lambda体(其实就是重写的抽象方法的方法体)使用:
Runnable r1 = () -> { System.out.println("Hello Lambda!"); };
Consumer con = (String str) -> { System.out.println(str); };
Consumer con = (str) -> { System.out.println(str); };
Consumer con = str -> { System.out.println(str); };
Comparat com = (x, y) -> { System.out.println("实现函数式接口方法!"); return Integer.compare(x, y); };
Comparator com = (x, y) -> Integer.compare(x, y);
Lambda举例1:
import org.junit.Test;
//Lambda表达式的使用举例
public class Sample {
@Test
public void test() {
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("我爱潮州湘子桥");
}
};
r1.run();//我爱潮州湘子桥
//使用Lambda后上述代码可以简化为:
Runnable r2 = () -> System.out.println("我爱潮州湘子桥");
r2.run();//我爱潮州湘子桥
}
}
Lambda举例2:
import org.junit.Test;
import java.util.Comparator;
//Lambda表达式的使用举例
public class Sample {
@Test
public void test() {
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
int compare1 = com1.compare(1, 2);
System.out.println(compare1);//-1
//使用Lamdba后上述代码可以简化为:
Comparator<Integer> com2 = (o1, o2) -> Integer.compare(o1, o2);
int compare2 = com2.compare(1, 2);
System.out.println(compare2);//-1
//使用方法引用后上述代码可以简化为:
Comparator<Integer> com3 = Integer::compare;
int compare3 = com3.compare(1, 2);
System.out.println(compare3);//-1
}
}
什么是函数式接口?
只包含一个抽象方法的接口,就称为函数式接口
可以通过 Lambda 表达式来创建该接口的对象。(若Lambda表达式抛出一个受检异常(即非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明)。
我们可以在一个接口上使用 @Functionallnterface 注解,这样做可以检查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
在java.util.function包下定义了Java8的丰富的函数式接口
如何理解函数式接口?
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer 消费型接口 |
T | void | 对T类型对象应用操作 void accpet(T t) |
Supplier 供给型接口 |
无 | T | 返回T类型对象 T get() |
Function 函数型接口 |
T | R | 对T类型对象应用操作,返回R类型对象 R apply(T, t) |
Predicate 断定型接口 |
T | boolean | 依据T类型对象是否满足某约束返回boolean值 boolean test(T t) |
Consumer接口示例:
import org.junit.Test;
import java.util.function.Consumer;
//Lambda表达式的使用举例
public class Sample {
public void happyTime(double money, Consumer<Double> con) {
con.accept(money);
}
@Test
public void test() {
//以前的写法
happyTime(500, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println("价格是:" + aDouble);//价格是:500.0
}
});
//使用Lambda表达式后的写法
happyTime(500, money -> System.out.println("价格是:" + money));//价格是:500.0
}
}
Predicate接口示例:
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
//Lambda表达式的使用举例
public class Sample {
//根据给定的规则,过滤集合中的字符串,此规则由Predicate的方法决定
public List<String> filterString(List<String> list, Predicate<String> pre) {
ArrayList<String> filterList = new ArrayList<>();
for (String s : list) {
if (pre.test(s)) {
filterList.add(s);
}
}
return filterList;
}
@Test
public void test() {
List<String> list = Arrays.asList("北京", "南京", "天津", "东京", "西京", "普京");
//以前的写法
List<String> filterStrs = filterString(list, new Predicate<String>() {
@Override
public boolean test(String s) {
return s.contains("京");
}
});
System.out.println(filterStrs);//[北京, 南京, 东京, 西京, 普京]
//使用Lambda表达式的写法
List<String> filterStrs1 = filterString(list, s -> s.contains("京"));
System.out.println(filterStrs1);//[北京, 南京, 东京, 西京, 普京]
}
}
方法引用三种主要使用情况的示例:
import org.junit.Test;
import java.io.PrintStream;
import java.util.Comparator;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
//方法引用的使用举例
public class Sample {
@Test
public void test() {
//情况一:对象 :: 实例方法
//Consumer中的void accept(T t)
//PrintStream中的void println(T t)
Consumer<String> con1 = str -> System.out.println(str);
con1.accept("北京");
//使用方法引用后的写法:
PrintStream ps = System.out;
Consumer<String> con2 = ps::println;
con2.accept("北京");
//Supplier中的T get()
//Employee中的String getName()
Employee emp = new Employee("Tom");
Supplier<String> sup1 = () -> emp.getName();
System.out.println(sup1.get());
//使用方法引用后的写法:
Supplier<String> sup2 = emp::getName;
System.out.println(sup2.get());
//情况二:类 :: 静态方法
//Comparator中的int compare(T t1, T t2)
//Integer中的int compare(T t1, T t2)
Comparator<Integer> com1 = (t1, t2) -> Integer.compare(t1, t2);
System.out.println(com1.compare(1, 2));
//使用方法引用后的写法:
Comparator<Integer> com2 = Integer::compare;
System.out.println(com2.compare(1, 2));
//Function中的R apply(T t)
//Math中的Long roung(Double d)
//最初的写法:
Function<Double, Long> func = new Function<Double, Long>() {
@Override
public Long apply(Double d) {
return Math.round(d);
}
};
//使用Lambda表达式后的写法:
Function<Double, Long> func1 = d -> Math.round(d);
System.out.println(func1.apply(12.3));
//使用方法引用后的写法:
Function<Double, Long> func2 = Math::round;
System.out.println(func2.apply(12.3));
//情况三:类 :: 实例方法
//对于情况三的理解:
//为什么抽象方法的参数列表和返回值类型与方法引用不同还能使用方法引用?
//因为抽象方法的方法体中是通过参数1的对象来调用方法
//那么在方法引用中其实就是将参数1当做调用方法的对象,直接传入除了参数1外的其他参数即可
//Comparator中的int compare(T t1, T t2)
//String中的int t1.compareTo(t2)
Comparator<String> com3 = (s1, s2) -> s1.compareTo(s2);
System.out.println(com3.compare("abc", "def"));
//使用方法引用后的写法:
Comparator<String> com4 = String::compareTo;
System.out.println(com4.compare("abc", "def"));
//BiPredicate中的boolean test(T t1, T t2)
//String中的boolean t1.equals(t2)
BiPredicate<String, String> pre1 = (s1, s2) -> s1.equals(s2);
System.out.println(pre1.test("abc", "def"));
//使用方法引用后的写法:
BiPredicate<String, String> pre2 = String::equals;
System.out.println(pre2.test("abc", "def"));
//Function中的R apply(T t)
//Employee中的String getName()
Function<Employee, String> func3 = e -> e.getName();
System.out.println(func3.apply(employee));
//使用方法引用后的写法:
Function<Employee, String> func4 = Employee::getName;
System.out.println(func4.apply(employee));
}
}
构造器引用的使用方法与方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致,抽象方法的返回值类型即为构造器所属的类的类型
示例代码:
import org.junit.Test;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
//构造器引用的使用举例
public class Sample {
@Test
public void test() {
//Supplier中的T get()
//Employee的空参构造器Employee()
//原本的写法:
Supplier<Employee> sup = new Supplier<Employee>() {
@Override
public Employee get() {
return new Employee();
}
};
System.out.println(sup.get());
//使用Lambda表达式后的写法:
Supplier<Employee> sup1 = () -> new Employee();
System.out.println(sup1.get());
//使用构造器引用后的写法:
Supplier<Employee> sup2 = Employee::new;
System.out.println(sup2.get());
//Function中的R apply(T t)
//Employee的构造器Employee(int id)
//使用Lambda表达式后的写法:
Function<Integer, Employee> func1 = id -> new Employee(id);
Employee employee = func1.apply(1001);
System.out.println(employee);
//使用构造器引用后的写法:
Function<Integer, Employee> func2 = Employee::new;
Employee employee1 = func2.apply(1001);
System.out.println(employee1);
//BiFunction中的R apply(T t, U u)
//Employee的构造器Employee(int id, String name)
//使用Lambda表达式后的写法:
BiFunction<Integer, String, Employee> func3 = (id, name) -> new Employee(id, name);
System.out.println(func3.apply(1001, "Tom"));
//使用构造器引用后的写法:
BiFunction<Integer, String, Employee> func4 = Employee::new;
System.out.println(func4.apply(1001, "Tom"));
}
}
可以把数组看做是一个特殊的类,用法与构造器引用一致
示例代码:
import org.junit.Test;
import java.util.Arrays;
import java.util.function.Function;
//数组引用的使用举例
public class Sample {
@Test
public void test() {
//Function中的R apply(T t)
//使用Lambda表达式的写法:
Function<Integer, String[]> func1 = length -> new String[length];
String[] arr1 = func1.apply(5);
System.out.println(Arrays.toString(arr1));
//使用数组引用的写法:
Function<Integer, String[]> func2 = String[]::new;
String[] arr2 = func2.apply(5);
System.out.println(Arrays.toString(arr2));
}
}
Java8中的Collection接口被扩展,提供了两个获取流的方法:
Java8中的Arrays的静态方法stream()可以获取数组流:
可以调用Stream类静态方法of(),通过显示值创建一个流。它可以接收任意数量的参数。
可以使用静态方法Stream.iterate()和Stream.generate(),创建无限流。
四种创建方式的示例代码:
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;
//测试Stream的实例化
public class Sample {
@Test
public void test() {
//通过集合
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
Stream<String> parallelStream = list.parallelStream();
//通过数组
int[] arr = new int[]{1, 2, 3, 4};
IntStream intStream = Arrays.stream(arr);
String[] strArr = new String[]{"AA", "BB", "CC"};
Stream<String> stringStream = Arrays.stream(strArr);
//通过Stream的of()
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4);
//创建无限流
//迭代 遍历前10个偶数
Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);
//生成 生成10个随机数
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
}
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
//测试Stream的中间操作
public class Sample {
@Test
public void test() {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 1);
//filter(Predicate p)
list.stream().filter(e -> e.intValue() > 2).forEach(System.out::print);//345
//limit(n)
list.stream().limit(3).forEach(System.out::print);//123
//skip(n)
list.stream().skip(3).forEach(System.out::print);//451
//distinct()
list.stream().distinct().forEach(System.out::print);//12345
}
}
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
//测试Stream的中间操作
public class Sample {
@Test
public void test() {
List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
//map(Function f)
list.stream().map(str -> str.toUpperCase()).forEach(System.out::print);//AABBCCDD
//faltMap(Function f)
//map和faltMap的区别类似于List中的add和addAll的区别
}
}
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
//测试Stream的中间操作
public class Sample {
@Test
public void test() {
//sorted()
List<Integer> list = Arrays.asList(4, 1, 3, 5, 6, 10);
list.stream().sorted().forEach(System.out::print);//1345610
//sorted(Comparator com)
list.stream().sorted((e1, e2) -> -Integer.compare(e1, e2)).forEach(System.out::print);//1065431
}
}
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
//测试Stream的终止操作
public class Sample {
@Test
public void test() {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
//allMatch(Predicate p)
boolean allMatch = list.stream().allMatch(e -> e.intValue() > 0);
System.out.println(allMatch);//true
//anyMatch(Predicate p)
boolean anyMatch = list.stream().anyMatch(e -> e.intValue() > 3);
System.out.println(anyMatch);//true
//noneMath(Predicate)
boolean noneMatch = list.stream().noneMatch(e -> e.intValue() < 0);
System.out.println(noneMatch);//true
//findFirst
Optional<Integer> first = list.stream().findFirst();
System.out.println(first);//Optional[1]
//findAny
Optional<Integer> any = list.parallelStream().findAny();
System.out.println(any);//Optional[3]
//count
long count = list.stream().filter(e -> e.intValue() > 2).count();
System.out.println(count);//3
//max(Comparator c)
Optional<Integer> max = list.stream().max(Integer::compare);
System.out.println(max);//Optional[5]
//min(Comparator c)
Optional<Integer> min = list.stream().min(Integer::compare);
System.out.println(min);//Optional[1]
//forEach(Consumer c) -- 内部迭代
list.stream().forEach(System.out::print);
}
}
备注:map和reduce的连接通常称为map-reduce模式,因为Google用它来进行网络搜索而出名
示例代码:
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
//测试Stream的终止操作
public class Sample {
@Test
public void test() {
//reduce(T identity, BinaryOperator)
//示例:计算1-10的自然数的和
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer sum = list.stream().reduce(0, Integer::sum);
System.out.println(sum);//55
//reduce(BinaryOperator)
Optional<Integer> sumOptional = list.stream().reduce(Integer::sum);
System.out.println(sumOptional);//Optional[55]
}
}
Collector接口中方法的实现决定了如何对流执行收集的操作(如收集到List、Set、Map)。
另外,Collectors实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:
示例代码:
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
//测试Stream的终止操作
public class Sample {
@Test
public void test() {
//collect(Collector c)
//示例:查找大于5的数,结果返回为一个List或Set
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> collect = list.stream().filter(e -> e.intValue() > 5).collect(Collectors.toList());
collect.forEach(System.out::print);//678910
Set<Integer> set = list.stream().filter(e -> e.intValue() > 5).collect(Collectors.toSet());
set.forEach(System.out::print);//678910
}
}
Optional类概述:
Optional类的使用: