lambda表达式
函数式编程关注的是具体的参数列表和方法体:
示例一
.
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("test新线程");
}
}).start();
/**优化为Lambda表达式*/
new Thread(()-> {
System.out.println("test新线程");
}).start();
示例二
/**优化某些匿名内部类*/
public void test02() {
int i = calculateNum(new IntBinaryOperator() {
@Override
public int applyAsInt(int left, int right) {
return left + right;
}
});
//优化后
int i1 = calculateNum((int left, int right)-> {
return left + right;
});
System.out.println(i);
}
public static int calculateNum(IntBinaryOperator operator){
int a =10;
int b =20;
return operator.applyAsInt(a,b);
}
匿名内部类可通过 快捷键alt+回车查看是否可直接简化为lambda表达式
并且可以再转换回去
省略规则:
- 参数类型可以省略
- 方法体中只有一句代码时大括号return和唯一一句代码的分号可以省略
- 方法只有一个参数时小括号可以省略
Stream流
准备工作:
public static List<Author> getAuthor() {
/** 数据初始化*/
Author author = new Author(1L,"张三",17,"一个平凡的律师",null);
Author author2 = new Author(2L,"李四",18,"一个法外狂徒",null);
Author author3 = new Author(3L,"王五",15,"一个无辜的群众",null);
Author author4 = new Author(3L,"王五",15,"一个无辜的群众",null);
ArrayList<Book> books1 = new ArrayList<>();
ArrayList<Book> books2 = new ArrayList<>();
ArrayList<Book> books3 = new ArrayList<>();
books1.add(new Book(1L,"刑法与自由","法律,哲学",88,"想把牢底坐穿"));
books1.add(new Book(2L,"诈骗咋骗","法律,社会学",88,"爱你孤身走暗巷"));
books2.add(new Book(3L,"赚钱的路子","法律,经济学",88,"你去看刑法啊"));
books2.add(new Book(4L,"坐牢的一生","法律,哲学",88,"我觉得我没机会坐的"));
books3.add(new Book(5L,"人情事故","法律,社会学",88,"有人情才会出事故"));
books3.add(new Book(6L,"炒币的都是傻逼","法律,经济学",88,"主要是我没赚钱"));
books3.add(new Book(7L,"马云说他不喜欢钱","经济学,哲学",88,"我喜欢钱"));
author.setBooks(books1);
author2.setBooks(books2);
author3.setBooks(books3);
author4.setBooks(books3);
ArrayList<Author> authors = new ArrayList<>(Arrays.asList(author, author2, author3, author4));
return authors;
}
示例一
public String hello(){
List<Author> authors = getAuthor();
authors.stream()
.distinct() //去重
.filter(author -> author.getAge()<18) //过滤18岁以上的
.forEach(author -> System.out.println(author.getName())); //循环打印姓名
return "hello";
}
流处理可通过Stream Trace 功能调试
补充: 快捷键ctrl+alt+m 抽取方法, 方法中使用ctrl+b 提示显示匿名内部类,或者new+空格提示
常规操作
创建流
示例一
/** 单列集合.stream() */
List<Author> authors = getAuthor();
Stream<Author> stream = authors.stream();
示例二
/** 数组 Arrays.stream(数组)或者Stream.of 来创建 */
Integer[]arr = {1,2,3,4,5};
Stream<Integer> stream1 = Arrays.stream(arr);
Stream<Integer> stream2 = Stream.of(arr);
stream1.distinct()
.filter(integer -> integer>2)
.forEach(integer -> System.out.println(integer));
示例三
/** 双列集合:转换成单列集合后再创建 */
HashMap<String, Integer> map = new HashMap<>();
map.put("网王王",25);
map.put("康康康",28);
map.put("牛牛牛",21);
Stream<Map.Entry<String, Integer>> stream3 = map.entrySet().stream();
stream3.filter(entry -> entry.getValue()>22)
.forEach(entry -> System.out.println(entry.getKey()+"=="+entry.getValue()));
中间操作
1.filter() 过滤 ,将结果为true的值返回到流 (示例已有)
2.map() 可以对流中的元素进行计算或转换。
map()方法中的第一个泛型不能改(第一个类型就是对象的元素,已经是定义好的,不能修改不然会报错),但是第二个泛型是可以修改的(可以修改为想要的数据类型),简单理解为就是把流当中的元素转换为另外一种元素类型然后再放到流中。
mapToInt() 高级用法…还有mapTOLong()等等,针对基本数据类型进行优化。
示例一
authors.stream().map(new Function<Author, String>() {
@Override
public String apply(Author author) {
return author.getName();
}
}).forEach(s -> System.out.println(s));
示例二
authors.stream()
.map(author -> author.getAge())
.map(age -> age+10)
.forEach(age -> System.out.println(age));
3.distinct() 去重
流中的distinct()是依赖Object中的equals方法来判断是否为相同对象的。所以使用distinct方法需要重写equals方法;
4.sorted() 排序
示例一
//对流中的元素按年龄进行降序排序
authors.stream()
.distinct()
.sorted(new Comparator<Author>() {
@Override
public int compare(Author o1, Author o2) {
return o2.getAge()-o1.getAge();
}
})
.forEach(author -> System.out.println(author));
5.limit() 可以设置流的长度,超出的部分将被抛弃
6.skip() 跳过流中的前N个元素,返回剩下的元素
7.flatMap() 可以把一个对象转换为多个对象放到流中
示例一
//未转换为lambda表达式
authors.stream().flatMap(new Function<Author, Stream<Book>>() {
@Override
public Stream<Book> apply(Author author) {
return author.getBooks().stream();
}
}).distinct()
.forEach(new Consumer<Book>() {
@Override
public void accept(Book book) {
System.out.println(book.getName());
}
});
示例二
//打印现有数据的所有分类,对分类进行去重
authors.stream()
.flatMap(author -> author.getBooks().stream())
.distinct()
.flatMap(book -> Arrays.stream(book.getCategoriy().split(",")))
.distinct()
.forEach(category -> System.out.println(category));
终结操作
流必须要要有终结操作,否则无法执行。流被终结以后无法再次使用
1.forEach() 对流中的元素进行遍历操作。
2.count() 返回此流中元素的计数。需要用一个数接收返回的值。
3.max/min() 获取流中的最值。
示例一
//获取这些书籍的最高分和最低分
Optional<Integer> max = authors.stream()
.flatMap(author -> author.getBooks().stream())
.map(book -> book.getScore())
.max((score1, score2) -> score1 - score2);
System.out.println(max.get());
4.collect() 是将流中的元素转为一个集合。
示例一
//获取一个存放所有作者名字的list
List<String> collect = authors.stream()
.map(author -> author.getName())
.collect(Collectors.toList());
System.out.println(collect);
示例二
//获取一个所有书名的集合
Set<String> books = authors.stream()
.flatMap(author -> author.getBooks().stream())
.map(book -> book.getName())
.collect(Collectors.toSet());
System.out.println(books);
示例三
//获取一个map集合,map的key为作者名,value为List<Book>
Map<String, List<Book>> map = authors.stream()
.distinct()
.collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks()));
System.out.println(map);
5.anyMatch() 根据逻辑判断返回布尔值。
示例一
//判断是否有年龄在29以上的作家
boolean flag = authors.stream()
.anyMatch(author -> author.getAge() > 29);
System.out.println(flag);
6.allMatch() 可以用来判断是否都符合逻辑判断条件,返回布尔值。
7.noneMatch() 可以用来判断是否都卢符合逻辑判断条件,返回布尔值。
8.findAny() 查找流中的任意一元素该方法无法保证获取的元素一定是流中的第一个元素。ifPresent() 如果Optional对象不为null则继续往下执行。为null就不继续往下执行。
示例一
//获取任意一个年龄大于18的作者
Optional<Author> optionalAuthor = authors.stream()
.filter(author -> author.getAge() > 18)
.findAny();
optionalAuthor.ifPresent(author -> System.out.println(author.getName()));
9.findFirst() 获取流中的第一个元素。
示例一
//获取一个年龄最小的作家
Optional<Author> first = authors.stream().sorted(new Comparator<Author>() {
@Override
public int compare(Author o1, Author o2) {
return o1.getAge() - o2.getAge();
}
}).findFirst();
first.ifPresent(author -> System.out.println(author.getName()));
10.reduce()传入一个参数,循环流中元素进行操作(可以对传入的参数进行比较等操作),并返回一个传入参数类型的数据。reduce两个参数的重载形式内部的计算方式如下:
T result = identity;
for(T element : this stream){
result = accumlator..apply(result,element);
}
return result;
示例一
//使用reduce求所有作者年龄的和(两个参数 使用BinaryOperator())
//第一个参数0,为起始值,然后传入两个参数,开始运算
Integer sum = authors.stream()
.map(author -> author.getAge())
.reduce(0, (result, element) -> result+element);
System.out.println(sum);
示例二
//使用reduce求所有作者中年龄的最大值
Integer max = authors.stream()
.map(author -> author.getAge())
.reduce(Integer.MIN_VALUE, (result, element) -> result < element ? element : result);
System.out.println(max);
示例三
//使用reduce求所有作者中年龄的最小值(一个参数的重载形式)
Optional<Integer> optionalInteger = authors.stream()
.map(author -> author.getAge())
.reduce((result, element) -> result < element ? result : element);
optionalInteger.ifPresent(age -> System.out.println(age));
第三种重载方式需要三个参数涉及并行流
Optional
养成使用Optional的习惯可以写出更加优雅的代码来避免空指针异常。
实际开发过程中很多数据是通过数据库来获取的,Mybatis从3.5版本以后也支持Optional了。可以直接把dao方法的返回值类型定义为Optional类型,Mybatis会自己把数据封装为Optional对象返回。封装的过程也不需要我们自己去操作。
很多函数式编程相关的API都用了Optional。
Optional.ofNullable() 将对象封装为Optional对象。无论传入的参数是否为null都不会出现问题。(建议使用 )
Optional.of() 传入的参数必须不能为null。(不建议使用)
示例一
Optional<Author> authorOptional = Optional.ofNullable(authors.get(0));
//进行一个安全消费
authorOptional.ifPresent(author -> System.out.println(author.getName()));
Optional.empty() 返回一个空的Optional对象。
Optional.ifPresent() 该方法会判断其内部封装的数据是否为空,不为空的时候才能执行具体的消费代码。
Optional.isPresent() 该方法会判断其内部封装的数据是否为空,为空返回false,不为空返回true.
Optional.filter() 在方法中进行逻辑判断,如果满足会返回Optional对象;不满足则返回null.
Optional.map() 将对象中的值转为Optional<List>对象.
如果想要安全的获取Optional对象中的值,不推荐使用get()方法。推荐使用以下几种方法。
Optional.orElseGet() 如果Optional中的值为null,可以自定义返回一个对象。
Optional.orElseThrow() 如果Optional中的值为null,可以手动抛出异常。
函数式接口
概况:
只有一个抽象方法的接口我们称之为函数式接口。
@FunctionalInterface注解进行标识,无论是否加上该注解只要接口中只有一个抽象方法,都是函数式接口。
常见函数式接口:
JDK自带的常用函数式接口
Comparator 消费接口
Function 计算转换接口
Predicate 判断接口
Supplier 生产型接口
函数式接口中的默认方法:
方法引用
方法引用的基本格式
类名或对象名::方法名
修改为方法引用写法:
修改后:
引用类的静态方法
前提:如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了某个类的静态方法,并且我们把要重写的抽象方法中所有的参数都按照顺序传入了这个静态方法中,这个时候我们就可以引用类的静态方法。
示例一
//转换前
authors.stream().map(Author::getAge)
.map(new Function<Integer, String>() {
@Override
public String apply(Integer integer) {
return String.valueOf(integer);
}
});
//转换后
authors.stream().map(Author::getAge)
.map(String::valueOf);
引用对象的实例方法
示例一
StringBuilder sb = new StringBuilder();
authors.stream().map(Author::getName)
.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
sb.append(s);
}
});
//转换后
authors.stream().map(Author::getName)
.forEach(sb::append);
引用类的实例方法
前提:如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了第一个参数的成员方法 ,并且我们把要重写的抽象方法中剩余的所有的参数都按照顺序传入了这个成员方法中,这个时候我们就可以引用类的实例方法。|
示例一
interface UseString{
String use(String str,int start,int lengtg);
}
public static String subName(String str,UseString useString){
int start = 0;
int length = 1;
return useString.use(str,start,length);
}
public static void main(String[] args) {
subName("爪", new UseString() {
@Override
public String use(String str, int start, int lengtg) {
return str.substring(start,lengtg);
}
});
}
//转换后
public static void main(String[] args) {
subName("爪", String::substring);
}
构造器引用
前提:如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了某个类的构造方法,并且我们把要重写的抽象方法中的所有的参数都按照顺序传入了这个构造方法中,这个时候我们就可以引用构造器。
authors.stream()
.map(author -> author.getName())
.map(new Function<String, StringBuilder>() {
@Override
public StringBuilder apply(String name) {
return new StringBuilder(name);
}
})
.forEach(stringBuilder -> System.out.println(stringBuilder));
//转换后
authors.stream()
.map(Author::getName)
.map(name -> new StringBuilder(name))
.forEach(System.out::println);
高级用法
基本数据类型优化
例如:mapToInt()针对基本数据类型进行优化,减少自动拆装箱过程
authors.stream().mapToInt(author -> author.getAge())
.map(age -> age+10)
.filter(age -> age>18)
.map(age -> age+2)
.forEach(System.out::println);
并行流
当流中有大量元素,可以使用并行流提高操作效率。其实并行流就是把任务分配给多个线程去完成。如果使用Stream的话,只需要修改一个方法的调用就可以使用并行流来提高效率
Stream.parallel()方法
parallelStream() 直接获取并行流
authors.stream().parallel()
.peek(new Consumer<Author>() {
@Override
public void accept(Author author) {
//获取当前操作的线程名称
System.out.println(author.getName()+Thread.currentThread().getName());
}
})
.mapToInt(author -> author.getAge())
.map(age -> age+10)
.filter(age -> age>18)
.map(age -> age+2)
.forEach(System.out::println);