jdk8中的语法糖,函数式编程思想的体现
可推导可省略
(参数列表)->{代码}
//匿名内部类写法
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程run方法执行");
}
}).start();
//lambda写法
new Thread(()->{
System.out.println("run方法执行");
}).start();
public static void printNum(IntPredicate predicate){//参数为java自带的接口
int[] arr = {1,2,3,4,5,6,7,8,9,10};
for (int i : arr) {
if (predicate.test(i)){
System.out.println(i);
}
}
}
内部类写法
printNum(new IntPredicate() {
@Override
public boolean test(int value) {
return value%2==0;
}
});
lambda写法
printNum((a)->a%2==0);//输出数组中为偶数的数
public static <R> R typeConver(Function<String,R> function){
String str = "1235";
R result = function.apply(str);
return result;
}
内部类写法
Integer integer = typeConver(new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.valueOf(s);
}
});
lambda写法
Integer integer1 = typeConver((s) -> Integer.valueOf(s));
java8的stream使用的是函数式编程模式,如同名字一样,可以对集合或数组进行链状流式的操作,可以更方便的让我们对集合或数组操作
单列集合:集合对象.stream()
List<Author> authors = getAuthors();
Stream<Author> stream = authors.stream();
数组:Arrays.stream(数组)
或 Stream.of(数组)
int[] arr = {1,2,3,4,5};
IntStream stream = Arrays.stream(arr);
Stream<int[]> arr1 = Stream.of(arr);
双列集合
Map<String,Integer> map = new HashMap<>();
map.put("1",1);
map.put("2",2);
map.put("3",3);
Stream<Map.Entry<String, Integer>> stream = map.entrySet().stream();
对流中元素进行条件过滤,符合条件的才能继续留在流中
例如
int[] arr = {1,2,3,4,5};
IntStream stream = Arrays.stream(arr);
stream
.filter(a->a>3)//大于3的符合条件,可以留在流中
.forEach(System.out::println);
可以对流中的元素进行计算或转换
authors.stream()
.distinct()
.map(author -> author.getName())//原本存的是author对象,全部转换成String类型的name属性值
.forEach(System.out::println);
去除流中的重复元素,依赖Object的equals方法来判断,需要重写equals方法
直接调用,没有参数
可以对流中元素进行排序
authors.stream()
.distinct()
.sorted((o1, o2) -> o2.getAge()- o1.getAge())//o1代表当前
.forEach(System.out::println);
如果调用空参的sorted方法,需要流中元素实现Comparable接口
有参的sorted方法即把排序方法搬到了参数中
设置流的最大长度,超出的部分将被抛弃
authors.stream()
.distinct()
.sorted((o1, o2) -> o2.getAge()- o1.getAge())
.limit(2)//限制只有两个元素,多余的被抛弃
.map(Author::getName)
.forEach(System.out::println);
跳过流中的前n个元素,返回剩下的元素
authors.stream()
.distinct()
.sorted()
.skip(1)//跳过第一个
.forEach(System.out::println);
map只能把一个对象转换成另一个对象来作为流中的元素。而flatMap可以把一个对象转换成多个对象作为流中的元素
authors.stream()//每个author中有一个book集合
.flatMap(author -> author.getBooks().stream())//获取所有的book对象,并整合成新流
.distinct()
.forEach(System.out::println);
authors.stream()
.flatMap(author -> author.getBooks().stream())
.flatMap(book ->Arrays.stream(book.getCategory().split(",")) )//按照,进行切分,并转换成流
.distinct()
.forEach(System.out::println);
对流中的元素进行遍历操作,我们通过传入的参数去指定遍历到的元素进行什么具体操作
用来获取当前流中元素的个数
long count = authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.count();//获取个数
可以用来获取流中的最值
authors.stream()
.flatMap(author -> author.getBooks().stream())
.map(book -> book.getScore())
.max((o1, o2) -> o1-o2);//需要传入比较器
把流转换成集合
list集合
List<String> collect = authors.stream()
.map(author -> author.getName())
.distinct()
.collect(Collectors.toList());//通过工具类
set集合
Set<Book> collect1 = authors.stream()
.flatMap(author -> author.getBooks().stream())
.collect(Collectors.toSet());
map集合
Map<String, List<Book>> collect = authors.stream()
.collect(Collectors.toMap(key -> key.getName(), value -> value.getBooks()));
可以用来判断是否有任意符合匹配条件的元素,结果为boolean类型
boolean b = authors.stream()
.distinct()
.anyMatch(author -> author.getAge() > 29);//判断是否有年龄大于29的数据
可以用来判断是否都符合匹配条件,结果为Boolean类型,都符合为true,否则为false
boolean b = authors.stream()
.allMatch(author -> author.getAge() > 18);//判断是否所有数据的年龄都大于18
可以判断流中的元素是否都不符合匹配条件,都不符合返回true,否则false
boolean b = authors.stream()
.noneMatch(author -> author.getAge() > 100);//判断是否所有数据的年龄都不大于100
获取流中任意一个元素,该方法没有办法保证获取的一定是流中的第一个元素
Optional<Author> any = authors.stream()
.filter(author -> author.getAge() > 18)
.findAny();//findAny不可以传参数,有筛选条件要先执行
获取流中的第一个元素
同上
对流中的数据按照你制定的计算方式计算出一个结果(缩减操作)
reduce的作用是把stream中的元素给组合起来,我们可以传入一个初始值,他会按照我们的计算方式依次拿流中的元素和在初始化值的基础上进行计算,计算结果再和后面的元素计算
Integer reduce = authors.stream()
.map(author -> author.getAge())
.reduce(0, (result, element) -> result + element);//第一个为初始值,后面为要进行的操作
//使用reduce求年龄的最大值
Integer reduce = authors.stream()
.map(author -> author.getAge())
.reduce(Integer.MIN_VALUE, (o1, o2) -> o2 > o1 ? o2 : o1);
reduce一个参数的重载形式
初始值默认为null,计算时如果初始值为null,则将第一个值赋给初始值,否则执行给定计算
authors.stream()
.map(author -> author.getAge())
.reduce((o1,o2)->o2>o1?o2:o1);
用于避免空指针异常
Optional就好像是包装类,可以把具体的数据封装Optional对象内部,然后我们去使用Optional中封装好的方法操作封装进去的数据就可以非常优雅的避免空指针异常
一般使用Optional的静态方法ofNullable来把数据封装成一个Optional对象,无论传入的参数是否为null都不会出问题
Author author = getAuthor();
Optional<Author> author1 = Optional.ofNullable(author);
如果确定对象不为空,则可以使用静态方法of
来封装。此时如果传入null,会报空指针
如果需要封装null到Optional中,可以使用empty
方法(此方法无参数)
使用ifPresent()方法,如果值为空则不执行任何操作,不为空则执行传入的代码
Optional<Author> author1 = Optional.ofNullable(author);
author1.ifPresent(a-> System.out.println(a.getName()));
可以使用get方法获取,当Optional内的值为空时,此方法会报异常
获取数据并且设置数据为空时的默认值,如果数据不为空就能获取到该数据,如果为空则根据你传入的参数来创建对象作为默认值返回
Optional<Author> author1 = Optional.ofNullable(author);
Author author2 = author1.orElseGet(() -> new Author());//此处为null的话会新建一个对象返回
获取数据,如果数据不为空就能获取到数据,如果为空则根据你传入的参数来创建异常抛出
Optional<Author> author1 = Optional.ofNullable(author);
Author author3 = author1.orElseThrow(() -> new RuntimeException());//如果为空,抛出运行时异常
可以使用filter()方法对数据进行过滤,如果原本有数据,但是不符合判断,也会变成一个无数据的Optional对象
Optional<Author> author1 = Optional.ofNullable(author);
author1.filter(a -> a.getAge() > 18).ifPresent(a-> System.out.println(a.getName()));
可以使用isPresent进行是否存在数据的判断,如果为空返回值为false,如果不为空,返回值为true。也可以使用ifPresent方法
Optional提供了map方法可以让我们对数据进行转换,并且转换得到的数据也还是被Optional包装好的
只有一个抽象方法的接口我们称之为函数接口
jdk的函数式接口都加了@FunctionalInterface
注解进行标识
Comsumer接口,用于消费
Function计算转换接口
Predicate判断接口
Supplier生产型接口
使用predict接口时可能需要进行判断条件的拼接,而and方法相当于使用&&来拼接两个判断条件
匿名内部类写法
//打印作家中年龄大于17并且姓名大于1的作家
authors.stream()
.filter(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getAge()>17;
}
}.and(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getName().length()>1;
}
})).forEach(System.out::println);
lambda写法
authors.stream()
.filter(((Predicate<Author>) author -> author.getAge() > 17)
.and(author -> author.getName().length()>1))
.forEach(System.out::println);
相当于用||拼接两个条件
authors.stream()
.filter(((Predicate<Author>)author -> author.getAge()>17).or(author -> author.getName().length()<2))
.forEach(System.out::println);
表示取反
authors.stream()
.filter(((Predicate<Author>)author->author.getAge()>17).negate())
.forEach(System.out::println);
使用lambda时,如果方法体中只有一个方法的调用的话,可以用方法引用简化代码
类名或对象名::方法名
map方法转换后对象为包装类,如后面要进行运算则会执行自动拆箱和装箱,可能影响效率
可以使用mapToxxx方法,xxx为数据类型,flatMap也有类似方法
当流中有大量元素时,我们可以使用并行流去提高操作效率,其实并行流就是把任务分配给多个线程去执行
创建流后调用parallel()
方法即可