为什么使用函数式编程?
- 首先工作中,大家都在使用函数式编程
- 大数量处理集合效率高
- 代码可读性高
- 消灭嵌套地狱
- 代码简洁,接近于自然语言
- 易于并发编程
Stream流
java8的Stream使用的是函数式编程模式,如同它的名字一样,它可以被用来对集合或者数组进行链状流式操作。可以方便的让我们的对集合或者数组操作;
常用操作
一定需要有终结操作
1.创建流
- 单列集合:集合对象.stream()
List<Author> authors = getAuthors();
Stream<Author> stream = authors.stream();
- 数组:Arrays.stream(数组)或者使用Stream.of创建
Integer [] arr ={1,2,3,4};
Stream<Integer> stream = Arrays.stream(arr);
Stream<Integer> stream1 = Stream.of(arr);
- 双列集合:转换成单列集合后再创建
Map<String, Integer> map = new HashMap<>();
map.put("a",11);
map.put("b",12);
map.put("c",13);
Set<Map.Entry<String, Integer>> entries = map.entrySet();
Stream<Map.Entry<String, Integer>> stream = entries.stream();
2.中间操作
- filter:可以对流中的元素进行过滤,符合条件的才保留;
List<Author> authors = getAuthors();
//打印年龄小于18的名字,并且去重
authors.stream()
//筛选
.filter(author -> author.getAge()<18)
//遍历
.forEach(author -> System.out.println(author.getName()));
- map:将流中的元素进行计算或者转换
List<Author> authors = getAuthors();
authors.stream()
.map(author -> author.getAge())
.map(age->age+10)
.forEach(age-> System.out.println(age));
- distinct :可以去除流中的重复元素 依赖的式object的equals方法来判断是否是相同对象的,所以要注意重写equals方法
List<Author> authors = getAuthors();
authors.stream()
//去重
.distinct()
//遍历
.forEach(author -> System.out.println(author.getName()));
- sorted 可以对流中的元素进行排序 如果使用sorted空参形式是需要实现Comparable
List<Author> authors = getAuthors();
authors.stream()
//去重
.distinct()
.sorted((o1, o2) -> o1.getAge()-o2.getAge())
//遍历
.forEach(author -> System.out.println(author.getAge()));
- limit 可以设流的最大长度,超出部分将抛弃
List<Author> authors = getAuthors();
authors.stream()
//去重
.distinct()
//排序
.sorted((o1, o2) -> o1.getAge()-o2.getAge())
//限制长度
.limit(2)
//遍历
.forEach(author -> System.out.println(author.getAge()));
- skip 跳过流中前n个元素
List<Author> authors = getAuthors();
authors.stream()
//去重
.distinct()
//排序
.sorted((o1, o2) -> o1.getAge()-o2.getAge())
//跳过第一元素
.skip(1)
//遍历
.forEach(author -> System.out.println(author.getAge()));
- flatMap map只能把一个对象转换为另一对象来作为流中的元素,而flatMap 可以把一个对象转化为多个对象作为流中的元素
List<Author> authors = getAuthors();
authors.stream()
.flatMap(author -> author.getBooks().stream())
.forEach(booK -> System.out.println(booK));
}
3.终结操作
- forEach 对流中的元素进行遍历操作,我们通过传入的参数去指定对遍历到的元素进行什么具体操作
- count 可以获得当前流中元素的个数
- max&min可以获得流的最值
- collect 把当前流转为一个集合
List<Author> authors = getAuthors();
authors.stream()
.map(author -> author.getName())
.collect(Collectors.toList());
List<Author> authors = getAuthors();
authors.stream()
.collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks()));
- 查找与匹配
- anyMatch 判断是否有任意匹配条件的元素,结果是boolean
- allMatch 判断是否都匹配条件, 结果是boolean
- noneMatch 判断是否都不匹配条件, 结果是boolean
- findAny获取流中任意一个元素
- findFirst获取流中第一个元素
- reduce归并 对流中的数据按照你指定的计算方式计算结果。(缩减操作)
一般为 map reduce形式
//将年龄相加
List<Author> authors = getAuthors();
Integer reduce = authors.stream()
.distinct()
.map(author -> author.getAge())
.reduce(0, (integer, integer2) -> integer + integer2);
System.out.println(reduce);
Optional
我们在编写代码时经常会出现空指针异常,所以在很多情况下我们需要做出各种非空的判断。
在jdk8中引入了Optional,可以极大程度的避免空指针异常。
使用
1.创建对象
Optional就好像是包装类,可以把我们的具体数据封装Optional对象内部,然后使用Optional中封装好的方法操作封装进去的数据可以非常优雅的避免空指针异常。
Author author = getAuthor();
Optional<Author> author1 = Optional.ofNullable(author);
author1.ifPresent(author2 -> System.out.println(author2.getName()));
或者直接封装到方法上,统一格式
private static Optional<Author> getAuthor(){
Author author = new Author(1L, "a", 13, "dadadadea", null);
return Optional.ofNullable(author);
}
public static void main(String[] args) {
Optional<Author> author = getAuthor();
author.ifPresent(author1 -> System.out.println(author1.getName()));
}
2.安全消费值
我们可以使用ifPresent方法,这个方法会判断其内封装的数据是否为空,不为空时才会执行具体的消费代码,这样更安全。
3.安全的获取值
获取到Optional返回的值,不推荐使用get方法
推荐使用orElseGet或orElseThrow
- orElseGet 获取数据并且设置数据为空时的默认值,如果数据不为空就能获取到该数据,为空则根据你传入的参数来创建对象作为默认值的返回。
Optional<Author> author = getAuthor();
//有值就返回,没有值就返回默认值
Author author1 = author.orElseGet(() -> new Author());
System.out.println(author1.getName());
- orElseThrow 获取数据,如果数据不为空就能获得该数据。如果为空则根据你传入的参数来创建异常抛出。
Optional<Author> author = getAuthor();
//有值就返回,没有值就抛出自定义的异常
try {
author.orElseThrow((Supplier<Throwable>) () -> new RuntimeException("数据异常"));
} catch (Throwable throwable) {
throwable.printStackTrace();
}
4.过滤
可以使用filter方法对数据进行过滤。如果原本是有数据,但不符合判断,就会变成一个无数据的optional对象。
Optional<Author> author = getAuthor();
author.filter(author1 -> author1.getAge() > 10).ifPresent(author1 -> System.out.println(author1.getName()));
5.判断
我们可以使用ifPresent方法进行判断,这个方法会判断其内封装的数据是否为空,不为空时,返回true。
Optional<Author> author = getAuthor();
if (author.isPresent()){
System.out.println(author.get().getName());
}
6.数据转换
数据转换,我们可以使用map,转换后也是Optional包装好的,保证了我们使用更安全。
Optional<Author> author = getAuthor();
author.map(author1 -> author1.getBooks())
.ifPresent(booKS -> System.out.println(booKS));
函数式接口
定义:只有一个抽象方法的接口称之为函数接口。
常见函数接口
- Consumer消费接口
- Function计算转换接口
- Predicate判断接口
- Supplier生产型接口
方法引用
我们在使用lambda时,如果方法体中只有一个方法的调用的话,我们可以用方法引用进一步简化代码。
格式:类名或这对象名::方法名
List<Author> authors = getAuthors();
authors.stream()
.map(Author::getName)
.collect(Collectors.toList());