标签: java lambda
Java8新特性—lambda表达式
对lambda介绍
1. lambda 表达式,通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用,也就是指匿名函数。
2. 在Java 8里面,所有的Lambda的类型都是一个接口,而Lambda表达式本身,也就是”那段代码“,需要是这个接口的实现。
lambda表达式基础语法
1. 关于箭头操作符:
Java8中引入了一个新的操作符,"->",该操作符称为箭头操作符或者Lambda操作符,箭头操作符将Lambda表达式拆分成两部分;
2. 左侧:
Lambda表达式的参数列表,对应的是接口中抽象方法的参数列表;
3. 右侧:
Lambda表达式中所需要执行的功能(Lambda体),对应的是对抽象方法的实现;(函数式接口(只能有一个抽象方法))
4. Lambda表达式的实质--对接口的实现;
语法格式:
- 接口中的抽象方法 : 无参数,无返回值;
例如:runnable接口中的方法,使用lambda实现方式Runnable r1 = () -> System.out.println("Hello world!");
- 接口中的抽象方法 : 无参数,无返回值;
- 接口中的抽象方法 : 一个参数且无返回值; (若只有一个参数,那么小括号可以省略不写)
例如:下面两种方式都是正确的方式// Consumer<String>con = (x) -> System.out.println(x); Consumer<String>con = x -> System.out.println(x);
- 两个参数,有返回值,并且有多条语句 : 要用大括号括起来,而且要写上return
例如:Comparator<Integer>com = (x,y) -> { System.out.println("函数式接口"); return Integer.compare(y,x); //降序 };
- 两个参数,有返回值,但是只有一条语句: 大括号省略,return省略
例如:Comparator<Integer>com = (x,y) -> Integer.compare(x,y);//升序
- Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出数据类型,即”类 型推断”,
例如:(Integer x,Integer y ) -> Integer.compare(x,y)可以简写成(x,y) -> Integer.compare(x,y);
lambda表达式函数式接口
函数式接口表示在接口内只有一个抽象方法;可以使用注解@FunctionlInterface来标识,可以检查是否是函数式接口;
Java中自带的有四个函数式接口:
(1)Consumer con 消费性接口 void accept(T t);
(2)Function fun 函数式接口 R appliy(T t);
(3)Predicate pre 断言形接口 boolean test(T t);
(4)Spplier sup 供给型接口 T get();
Java中所给的四个函数式接口可以直接使用,也可以使用自定义的函数式接口,但是必须按照函数式接口 规范定义,函数中只有一个方法,且使用注解检查是否符合函数式标准。
方法引用和构造器引用
1. 方法引用
使用前提: Lambda体中调用方法的参数列表和返回值类型,要和函数式接口中抽象方法的参数列表和返回 值类型保持一致;
2. 语法格式:
1)、对象::实例方法名
2)、类名::静态方法
3)、类::实例方法名
使用注意: 若Lambda参数列表中的第一个参数是实例方法的第一个调用者,而第二个参数是实例方法的参数时,可以使用ClassName :: method
3. 构造器引用
需要调用构造器的参数列表,要与函数式接口中的抽象方法的参数列表保持一致;
实体类中修饰时间戳
使用 LocalDateTime 并且加上注解@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableLogic 在实体类中标志逻辑删除
Stream
引入流
- 使用流与不使用流的区别
例子:需要筛选出一份菜单中卡路里<400的菜的名字
public class Code_01_Java7AndJava8Compare {
public static void main(String[] args) {
// 返回 热量<400 的菜肴 的 名称, 返回结果按照从低到高排序, Java7的写法
System.out.println(java7());
System.out.println(java8());
}
static List<String> java7(){
List<Dish> lowCaloricDishes = new ArrayList<>();
for (Dish d : Dish.menu) {
if (d.getCalories() < 400) {
lowCaloricDishes.add(d);
}
}
Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
public int compare(Dish d1, Dish d2) {
return Integer.compare(d1.getCalories(), d2.getCalories());
}
});
List<String> lowCaloricDishesName = new ArrayList<>();
for (Dish d : lowCaloricDishes) {
lowCaloricDishesName.add(d.getName());
}
return lowCaloricDishesName;
}
static List<String> java8(){
List<String> lowCaloricDishesName =
Dish.menu.stream()
.filter(d -> d.getCalories() < 400)
.sorted(Comparator.comparing(Dish::getCalories))
.map(Dish::getName)
.collect(Collectors.toList());
return lowCaloricDishesName;
}
}
- 流的概述
- 流简短的定义: 是数据渠道,用于操作数据源(集合,数组等)所生成的元素序列;
- 元素序列 — 就像集合一样,流也提供了一个接口,可以访问特定元素类型的一组有序 值。因为集合是数据结构,所以它的主要目的是以特定的时间/ 空间复杂度存储和访问元 素(如ArrayList 与 LinkedList) 。但流的目的在于表达计算,比如你前面见到的 filter、 sorted和 map。集合讲的是数据,流讲的是计算。
- 源 — 流会使用一个提供数据的源,如集合、数组或输入/输出资源;
- 数据处理操作 — 流的数据处理功能支持类似于数据库的操作,以及函数式编程语言中 的常用操作,如filter、 map、 reduce、 find、 match、 sort等;
- 流的重要的特点
- 流水线 — 很多流操作本身会返回一个流,这样多个操作就可以链接起来,形成一个大 的流水线;流水线的操作可以看作对数据源进行数据库式查询;
- 内部迭代 — 与使用迭代器显式迭代的集合不同,流的迭代操作是在背后进行的;
- ①Stream自己不会存储元素;② Stream不会改变原对象,相反,他们会返回一个持有结果的新Stream;③Stream操作是延迟执行的,这意味着他们会等到需要结果的时候才执行;
流与集合
1)、只能遍历一次
请注意,和迭代器类似,流只能遍历一次- 遍历完之后,我们就说这个流已经被消费掉了;
- 你可以从原始数据源那里再获得一个新的流来重新遍历一遍,就像迭代器一样(这里假设它是集 合之类的可重复的源,如果是I/O通道就没戏了);
2)、外部迭代与内部迭代 - 使用Collection接口需要用户去做迭代(比如用for-each) ,这称为外部迭代;
- 相反, Streams库使用内部迭代;
外部迭代和内部迭代的区别:
- 流操作
主要分为两种:一种为中间操作,另一种为终端操作;
1)、中间操作
中间操作就是产生的结果(仍然是一个流)。
诸如filter或 sorted等中间操作会返回另一个流。这让多个操作可以连接起来形成一个查询。
// 除非流水线上触发一个终端操作,否则中间操作不会执行任何处理
// 流的延迟性质。
public class Code_02_StreamDelayFeature {
public static void main(String[] args) {
List<String> names =
Dish.menu.stream()
.filter(d -> {
System.out.println("filtering" + d.getName());
return d.getCalories() > 300;
})
.map(d -> {
System.out.println("mapping" + d.getName());
return d.getName();
})
.limit(3)
.collect(Collectors.toList());
System.out.println(names);
// // 终端操作
// System.out.println("-------Terminal Operation------");
// Dish.menu.stream().forEach(System.out::println);
}
}
2)、终端操作
终止操作会从流的流水线生成结果。其结果是任何不是流的值,比如List、 Integer,甚至 void。
3)、流的使用步骤
流的流水线背后的理念类似于构建器模式。
三个基本步骤:
- 创建Stream : 需要一个数据源(如:集合,数组),获取一个流;
- 中间操作: 一个中间操作链,对数据源的数据进行处理;
- 终止操作(终端操作): 一个终止操作,执行中间操作链,并产生结果;
使用流
- 构建流
构建的流的方式有:
从Collection中构建;
从值value(Stream.of())中构建;
从数组中构建(Arrays.stream());
从文件中构建;
由函数生成: 创建无限流;
创建的几种方法的示例代码:
/** 创建流的几种方法 */
public class Code_03_CreateStream {
public static void main(String[] args) throws IOException {
PrintStream out = System.out;
out.println("--------fromCollection--------");
fromCollection().forEach(x -> out.print(x + " ")); // 从Collection中创建Stream
out.println("\n" + "-------fromValues---------");
fromValues().forEach(x -> out.print(x + " ")); // 从Collection中创建Stream
out.println("\n" + "--------fromArrays--------");
fromArrays().forEach(x -> out.print(x + " ")); // 从Collection中创建Stream
out.println("\n" + "--------fromFile--------");
fromFile().forEach(out::println); // 从函数中创建
out.println("\n" + "--------fromIterate--------");
fromIterate().forEach(x -> out.print(x + " ")); // 从函数中创建
out.println("\n" + "--------fromGenerate--------");
fromGenerate().forEach(x -> out.print(x + " ")); // 从函数中创建
out.println("\n" + "--------fromCustom--------");
fromCustom().forEach(x -> out.print(x + " "));
out.println("\n" + "----------------");
}
static Stream<String> fromCollection() {
List<String> list = Arrays.asList("aa", "bb", "cc");
return list.stream();
}
static Stream<String> fromValues() {
return Stream.of("aa", "bb", "cc");
}
static Stream<String> fromArrays() {
String[] str = {"aa", "bb", "cc"};
return Arrays.stream(str);
}
static Stream<String> fromFile() throws IOException {
Path path = Paths.get("/home/zxzxin/Main.java");
Stream<String> stream = Files.lines(path);
return stream;
}
static Stream fromIterate() {
return Stream.iterate(0, n -> n + 2).limit(5); // 函数创建的无限流
}
static Stream<Double> fromGenerate(){
return Stream.generate(Math::random).limit(5);
}
// 创建Custom的流 (CusSupplier)
static Stream<Custom>fromCustom(){
return Stream.generate(new CusSupplier()).limit(5);
}
static class CusSupplier implements Supplier<Custom> {
private int index = 0;
private Random random = new Random(System.currentTimeMillis());
@Override
public Custom get() {
index = random.nextInt(100);
return new Custom(index, "name-" + index);
}
}
static class Custom {
private int id;
private String name;
public Custom(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "Obj{" +
"name='" + name + '\'' +
", id=" + id +
'}';
}
}
}
输出:
--------fromCollection--------
aa bb cc
-------fromValues---------
aa bb cc
--------fromArrays--------
aa bb cc
--------fromFile--------
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(new BufferedInputStream(System.in));
PrintStream out = System.out;
}
}
--------fromIterate--------
0 2 4 6 8
--------fromGenerate--------
0.18018050075496417 0.948721748467966 0.37983036182518304 0.679145483357325 0.21520045208568783
--------fromCustom--------
Obj{name='name-73', id=73} Obj{name='name-84', id=84} Obj{name='name-14', id=14} Obj{name='name-79', id=79} Obj{name='name-51', id=51}
----------------
filter、limit、skip、map、flatMap
- filter : 该操作会接受一个谓词(一个返回boolean的函数)(Predicate)作为参数,并返回一个包括所有符合谓词的元素的流;
- limit : 流支持limit(n)方法,该方法会返回一个不超过给定长度的流;
- skip : 流还支持skip(n)方法,返回一个扔掉了前n 个元素的流;
- map : 流支持map方法,它会接受一个函数(Function)作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素(使用映射一词,是因为它和转换类似,但其中的细微差别在于它是“创建一个新版本”而不是去“修改”);
- flatMap (扁平化): flatmap()方法让你把一个流中的每个值都换成另一个流,然后把所有的流连接 起来成为一个流;
- match、find、reduce
- match:查看元素是否匹配(返回boolean),包括allMatch(), anyMatch()、noneMatch();
- find :
- isPresent()将在Optional包含值的时候返回true, 否则返回false;
- ifPresent(Consumer block)会在值存在的时候执行给定的代码块;
- T get()会在值存在时返回值,否则抛出一个NoSuchElement异常;
- T orElse(T other)会在值存在时返回值,否则返回一个默认值;
- Optional of(T value) : 通过value构造一个Optional;
public class Code_05_StreamOperations2 {
static PrintStream out;
public static void main(String[] args) {
out = System.out;
out.println("-------matchTest---------");
matchTest();
out.println("\n" + "-------findTest---------");
findTest();
out.println("\n" + "-------reduceTest---------");
reduceTest();
}
static void matchTest(){
List<Integer> arr = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
out.println(arr.stream().allMatch(i -> i > 10));
out.println(arr.stream().anyMatch(i -> i > 6));
out.println(arr.stream().noneMatch(i -> i < 0));
}
static void findTest(){
List<Integer> arr = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
Optional<Integer> any = arr.stream().filter(i -> i % 2 == 0).findAny();
out.println(any.get());
Optional<Integer> first = arr.stream().filter(i -> i % 2 == 0).findFirst();
first.ifPresent(out::println);
out.println(first.get()); //没有就抛出 NoSuchElementException
out.println(first.orElse(-1)); // 如果first为空就输出-1
System.out.println(first.filter(i -> i == 2).get()); // Optional还会产生一个stream
System.out.println(find(arr, -1, i -> i > 10)); // 自己写的一个防止空指针的,而Optional中有一个已经存在的
}
static int find(List<Integer> values, int defaultValue, Predicate<Integer> predicate){
for(int val : values){
if(predicate.test(val))
return val;
}
return defaultValue;
}
// reduce 也是一个terminal的操作
static void reduceTest(){
List<Integer> arr = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
System.out.println(arr.stream().reduce(0, (a, b) -> a + b)); //计算数组的和 ,有初始值就是Integer
arr.stream().reduce((a, b) -> a + b).ifPresent(out::println); // 没有初始值就是 Optional
// 提取所有的偶数相乘
int res = arr.stream().filter(x -> x%2 == 0).reduce(1, (a, b) -> a*b);
Optional.of(res).ifPresent(out::println);
}
}
输出:
-------matchTest---------
false
true
true
-------findTest---------
2
2
2
2
2
-1
-------reduceTest---------
28
28
48
- 数值流
Java 8引入了三个原始类型特化流接口来解决这个问题:IntStream、 DoubleStream和LongStream,分别将流中的元素特化为int、 long和 double,从而避免了暗含的装箱成本。这些特化的原因并不在于流的复杂性,而是装箱造成的复杂性——即类似int和 Integer之间的效率差异。
映射方法:
- 映射到数值流
- 将流转换为特化版本的常用方法是mapToInt、 mapToDouble和 mapToLong;
- 例如mapToInt返回一个IntStream(而不是一个Stream);
- 转换回对象流
- 使用boxed()方法;
- 用处: 例如,IntStream上的操作只能产生原始整数。
Optional取代null
用流收集数据
Java8中的时间表示
- 分为四类:String、Instant、LocalDateTime、ZonedDateTime
- String是格式化的时间,Instant是时间戳,LocalDateTime是不含时区信息的时间,ZonedDateTime是含有时区信息的时间
乐观锁 与 悲观锁
乐观锁
概述
乐观锁其实表示
悲观锁
作者 @zzxhub