@Test
public void test01(){
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello World!");
}
};
r.run();
}
Lambda是一个匿名函数,可以理解为一段可以传递的代码(将代码像数据一样传递);可以写出更简洁、更灵活的代码;作为一种更紧凑的代码风格,是Java语言表达能力得到提升
@Test
public void test02(){
Runnable r1 = () -> {
System.out.println("Hello Lambda");
};
r1.run();
}
基础语法:
Java8引入了一个新的操作符:
->
,该操作符称为箭头操作符或Lambda操作符,箭头操作符将Lambda表达式拆分为2部分左侧:Lambda表达式的
参数列表
右侧:Lambda表达式中
所执行的功能,即Lambda体
1、无参数,无返回值
() -> System.out.println("Hello Lambda!");
2、一个参数,无返回值
(x) -> System.out.println(x);
3、 一个参数,小括号可以省略不写
x -> System.out.println(x);
4、二个以上参数,有返回值,并且lambda体有多条语句
Comparator com = (x, y) -> {
System.out.println("函数式接口");
return Integer.compare(x, y);
};
5、Lambda 体中只有一条语句, return 和 大括号都可以省略不写
Comparator com = (x, y) -> Integer.compare(x, y);
5、Lambda 表达式 参数的数据类型可以省略不写 Jvm可以自动进行 “类型推断”
(Integer x, Integer y) -> Integer.compare(x, y);
6、Lambda表达式需要 函数式接口 的支持
函数式接口:接口中只有一个抽象方法的接口,称为函数式接口,用@FunctionalInterface
修饰
调用 Collections.sort() 方法,通过定制排序 比较两个 Employee (先按照年龄比,年龄相同按照姓名比),使用 Lambda 表达式作为参数传递
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
private Integer id;
private String name;
private Integer age;
private Double salary;
}
List emps = Arrays.asList(
new Employee(101, "Z3", 19, 9999.99),
new Employee(102, "L4", 20, 7777.77),
new Employee(103, "W5", 35, 6666.66),
new Employee(104, "Tom", 44, 1111.11),
new Employee(105, "Jerry", 60, 4444.44)
);
@Test
public void test01(){
Collections.sort(emps, (e1, e2) -> {
if (e1.getAge() == e2.getAge()){
return e1.getName().compareTo(e2.getName());
} else {
return Integer.compare(e1.getAge(), e2.getAge());
}
});
for (Employee emp : emps) {
System.out.println(emp);
}
}
定义:接口中只有一个抽象方法的接口 @FunctionalIterface
Java内置四大核心函数式接口:
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer 消费型接口 |
T | void | 对类型为T的对象应用操作:void accept(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) |
@Test
public void test01(){
//Consumer
Consumer consumer = (x) -> System.out.println("消费型接口" + x);
//test
consumer.accept(100);
}
@Test
public void test02(){
List list = new ArrayList<>();
List integers = Arrays.asList(1,2,3);
list.addAll(integers);
//Supplier
Supplier supplier = () -> (int)(Math.random() * 10);
list.add(supplier.get());
System.out.println(supplier);
for (Integer integer : list) {
System.out.println(integer);
}
}
@Test
public void test03(){
//Function
String oldStr = "abc123456xyz";
Function function = (s) -> s.substring(1, s.length()-1);
//test
System.out.println(function.apply(oldStr));
}
@Test
public void test04(){
//Predicate
Integer age = 35;
Predicate predicate = (i) -> i >= 35;
if (predicate.test(age)){
System.out.println("你该退休了");
} else {
System.out.println("我觉得还OK啦");
}
}
定义:若 Lambda 表达式体中的内容已有方法实现,则我们可以使用“方法引用”
语法格式:
public class BuilderTest {
//对象::实例方法
@Test
public void test(){
Consumer con = x -> {
System.out.println(x);
};
PrintStream out = System.out;
Consumer con1 = out::println;
Consumer con2 = System.out::println;
con2.accept("qwe");
}
//类::静态方法名
@Test
public void test1(){
Comparator com = (x, y)-> Integer.compare(x,y);
Comparator com1 = Integer::compare;
}
// 类::实例方法名
@Test
public void test2(){
BiPredicate bp = (x,y) -> x.equals(y);
BiPredicate bp2 = String::equals;
}
}
注意:
ClassName::method
Function function = new Function() {
@Override
public Integer apply(String s) {
return s.hashCode();
}
};
Function function2 = o -> o.hashCode();
Function function3 = String::hashCode;
// o -> o.hashCode() 等价与 String::hashCode
格式:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
private Integer id;
private String name;
private Integer age;
private Double salary;
public Employee(Integer age){
this.age = age;
}
}
@Test
public void test04(){
Supplier sup1 = () -> new ArrayList();
Supplier sup2 = ArrayList::new;
//如果有多个构造器,如何判断是调用的实体类的哪个构造器呢?
//看下面的注意,即Function(T,R)内部的函数 R apply(T t) 是1个参数,那么就会调用是1个参数的构造器。
Function fun2 =Employee::new;
}
注意:需要调用的构造器的参数列表要与函数时接口中抽象方法的参数列表保持一致
语法:
@Test
public void test(){
Function fun = x -> new String[x];
Function fun2 = String[]::new;
}
Stream的 3 个操作步骤
- 创建Stream
- 一个或多个中间操作,连接形成流水线
- 终止操作。除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。
@Test
public void test(){
// 1. 通过Collection系列集合的 stream() 方法或 parallelStream()
List list = new ArrayList<>();
Stream stream = list.stream();
// 2. 通过 Arrays 类中的静态方法 stream() 获取数组流
String[] strArr = new String[10];
Stream stream1 = Arrays.stream(strArr);
// 3. 通过 Stream 类中的静态方法 of()
Stream stream2 = Stream.of("aa", "bb", "cc");
// 4. 创建无限流,要有终止操作才有效果
// (1)迭代
Stream stream3 = Stream.iterate(0, (x) -> x + 2);
stream3.forEach(System.out::println); // 不停打印,停不下来
stream3.limit(10) // 中间操作
.forEach(System.out::println); // 终止操作
// (2)生成
Stream.generate(() -> new Random().nextInt(32))
.limit(32)
.forEach(System.out::println);
}
1、筛选和切片:
List emps = Arrays.asList(
new Employee(101, "Z3", 19, 9999.99),
new Employee(102, "L4", 20, 7777.77),
new Employee(103, "W5", 35, 6666.66),
new Employee(104, "Tom", 44, 1111.11),
new Employee(105, "Jerry", 60, 4444.44)
);
@Test
public void test01(){
emps.stream()
.filter((x) -> x.getAge() > 35)
.limit(3) //短路?达到满足不再内部迭代
.distinct()
.skip(1)
.forEach(System.out::println);
}
2、映射
@Test
public void test02(){
List list = Arrays.asList("a", "b", "c");
list.stream()
.map((str) -> str.toUpperCase())
.forEach(System.out::println);
}
public Stream filterCharacter(String str){
List list = new ArrayList<>();
for (char c : str.toCharArray()) {
list.add(c);
}
return list.stream();
}
@Test
public void test03(){
List list = Arrays.asList("a", "b", "c");
Test02 test02 = new Test02();
list.stream()
.flatMap(test02::filterCharacter)
.forEach(System.out::println);
}
3、排序
public class test {
@Test
public void test(){
//自然排序
List list = Arrays.asList("aaa", "eee", "ddd", "bbb");
list.stream().sorted().forEach(System.out::println);
//定制排序
List list1 = Arrays.asList(
new Person("张三", 18, 2000.0),
new Person("李四", 18, 5000.0),
new Person("王五", 45, 8700.0),
new Person("赵六", 42, 4200.0),
new Person("陈七", 56, 13100.0)
);
list1.stream().sorted((p1,p2) -> {
if (p1.getAge().equals(p2.getAge())){
return p1.getSale().compareTo(p2.getSale());
}else {
return p1.getAge().compareTo(p2.getAge());
}
}).forEach(System.out::println);
}
}
1、查找和匹配:
@Data
public class Person {
private String name;
private Integer age;
private Double sale;
private Status status;
public enum Status{
FREE,
BUSY,
VOCATION
};
public Person(String name, Integer age, Double sale, Status status) {
this.name = name;
this.age = age;
this.sale = sale;
this.status = status;
}
}
public class test {
@Test
public void test(){
List list = Arrays.asList(
new Person("张三", 18, 2000.0, Person.Status.BUSY),
new Person("李四", 18, 5000.0,Person.Status.FREE),
new Person("王五", 45, 8700.0,Person.Status.VOCATION),
new Person("赵六", 42, 4200.0,Person.Status.BUSY),
new Person("陈七", 56, 13100.0,Person.Status.BUSY)
);
//allMatch 检查是否匹配所有元素,返回值为Boolean类型
boolean b = list.stream().allMatch(e -> e.getStatus().equals(Person.Status.BUSY));
System.out.println(b); // false
//anyMatch 检查是否匹配至少一个元素,返回值为Boolean类型
boolean b1 = list.stream().anyMatch(e -> e.getStatus().equals(Person.Status.BUSY));
System.out.println(b1); // true
//noneMatch 检查是否没有匹配所有元素,返回值为Boolean类型
boolean b2 = list.stream().noneMatch(e -> e.getStatus().equals(Person.Status.BUSY));
System.out.println(b2); // false
//findFirst 返回第一个元素
//Optional 防止空指针异常的类型,如果first为null,可以使用.orelse()方法指定一个不为空的对象
Optional op1 = list.stream()
.sorted((e1, e2) -> Double.compare(e1.getSale(), e2.getSale())).findFirst();
System.out.println(op1.get()); // Person(name=张三, age=18, sale=2000.0, status=BUSY)
//findAny 返回当前流中的任意元素
//parallelStream 并行流,多个进程同时去进行filter、findAny,谁先找到算谁的
Optional op2 = list.parallelStream().filter(e -> e.getStatus().equals(Person.Status.FREE)).findAny();
System.out.println(op2.get()); // Person(name=李四, age=18, sale=5000.0, status=FREE)
//count 返回流中元素的总个数
long count = list.stream().count();
System.out.println(count); // 5
//max 返回流中的最大值
Optional max = list.stream().max((e1, e2) -> Double.compare(e1.getSale(), e2.getSale()));
System.out.println(max.get()); // Person(name=陈七, age=56, sale=13100.0, status=BUSY)
//min 返回流中的最小值
//返回list中的最小工资数
System.out.println(list.stream().map(Person::getSale).min(Double::compare).get()); // 2000.0
}
}
2、归约
// 求和
List list = Arrays.asList(1, 2, 3, 4, 5);
// Integer::sum ----> (x, y) -> x+y
// 一开始把 0 当作 x,然后从集合中取出一个元素当作 y,求和为 1
// 然后把 1 再当作 x,再从集合中取出一个元素当作 y,求和为 3
// ...
Integer sum = list.stream()
// 有0作为初始值,不可能为空
.reduce(0, Integer::sum);
System.out.println(sum);
Optional sum1 = list.stream()
// 没有初始值,可能为空,所以返回 Optional 对象
.reduce(Integer::sum);
System.out.println(sum1.get());
3、收集
@Data
public class Employee {
private Integer id;
private String name;
private Integer age;
private Double salary;
public Employee(Integer id, String name, Integer age, Double salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
}
public class 收集 {
List emps = Arrays.asList(
new Employee(1,"张三", 10, 9999.99),
new Employee(2,"李四", 40, 7777.77),
new Employee(3,"王五", 30, 6666.66),
new Employee(4,"赵六", 28, 1111.11),
new Employee(5,"老王", 37, 4444.44)
);
@Test
public void test02(){
//放入List
List list = emps.stream()
.map(Employee::getName)
.collect(Collectors.toList());
list.forEach(System.out::println);
//放入Set
Set set = emps.stream()
.map(Employee::getName)
.collect(Collectors.toSet());
set.forEach(System.out::println);
//放入LinkedHashSet
LinkedHashSet linkedHashSet = emps.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(LinkedHashSet::new));
linkedHashSet.forEach(System.out::println);
}
@Test
public void test03(){
//总数
Long count = emps.stream()
.collect(Collectors.counting());
System.out.println(count);
//平均值
Double avg = emps.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(avg);
//总和
Double sum = emps.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(sum);
//最大值
Optional max = emps.stream()
.collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
System.out.println(max.get());
//最小值
Optional min = emps.stream()
.map(Employee::getSalary)
.collect(Collectors.minBy(Double::compare));
System.out.println(min.get());
}
@Test
public void test04(){
//分组
Map> map = emps.stream()
.collect(Collectors.groupingBy(Employee::getId));
System.out.println(map);
//多级分组
Map>> mapMap = emps.stream()
.collect(Collectors.groupingBy(Employee::getId, Collectors.groupingBy((e) -> {
if (e.getAge() > 35) {
return "开除";
} else {
return "继续加班";
}
})));
System.out.println(mapMap);
//分区
Map> listMap = emps.stream()
.collect(Collectors.partitioningBy((e) -> e.getSalary() > 4321));
System.out.println(listMap);
}
@Test
public void test05(){
//总结
DoubleSummaryStatistics dss = emps.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getMax());
System.out.println(dss.getMin());
System.out.println(dss.getSum());
System.out.println(dss.getCount());
System.out.println(dss.getAverage());
//连接
String str = emps.stream()
.map(Employee::getName)
.collect(Collectors.joining("-")); //可传入分隔符
System.out.println(str);
}
}
stream和parallelStream的简单区分: stream是顺序流,由主线程按顺序对流执行操作,而parallelStream是并行流,内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求。如果流中的数据量足够大,并行流可以加快处速度。
例如筛选集合中的奇数,两者的处理不同之处:
利用Fork/Join进行计算和
public class ForkJoinCalculate extends RecursiveTask {
private static final long serialVersionUID = 12313435L;
private long start;
private long end;
private static final long THRESHOLD = 10000;
public ForkJoinCalculate(long start,long end){
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
long length = end - start;
if (length <= THRESHOLD){
long sum = 0;
for (long i = start; i <= end ; i++) {
sum+= i;
}
return sum;
}else {
long middle = (start + end )/2;
ForkJoinCalculate left = new ForkJoinCalculate(start,middle);
left.fork(); //拆分子任务,同时压入线程队列
ForkJoinCalculate right = new ForkJoinCalculate(middle + 1,end);
right.fork();
return left.join() + right.join();
}
}
}
public class TestForkJoin {
@Test
public void test1(){
Instant start = Instant.now();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinCalculate forkJoinCalculate = new ForkJoinCalculate(0, 1000000000L);
Long sum = forkJoinPool.invoke(forkJoinCalculate);
System.out.println(sum);
Instant end = Instant.now();
System.out.println("消耗时间:" + Duration.between(start,end).toMillis()); //393
}
//普通for循环计算
@Test
public void test2(){
Instant start = Instant.now();
long sum = 0L;
for (int i = 0; i < 1000000000L; i++) {
sum += i;
}
System.out.println(sum);
Instant end = Instant.now();
System.out.println("消耗时间:" + Duration.between(start,end).toMillis()); //538
}
}
Java8并行流例子
//串行流(单线程):切换为并行流 parallel()
//并行流:切换为串行流 sequential()
// 结果会溢出,不需要理会。运行时,注意观察CPU状态。运算会比普通for省时
LongStream.rangeClosed(0, 100000000L)
.parallel() //底层:ForkJoin
.reduce(0, Long::sum);
// 如果是数据较少,串行地情况下,一般会返回第一个结果
// 如果是并行的情况,那就不能确保是第一个。
// 注意,使用并行流时要考虑,并行操作对结果是否有影响
Optional emp2 = emps.parallelStream() // 并行流
.filter((e) -> e.getStatus().equals(Employee.Status.FREE))
.findAny();
System.out.println(emp2.get());