目录
如果你发现虽然你的业务代码千差万別,但是它们拥有同样的准备和清理阶段,这时,你完全可以将这部分代码用 Lambda实现。
比如文件流处理异或异常
1.函数接口
1.1定义
- 被@FunctionalInterface注释的接口,满足@FunctionalInterface注释的约束。
- 没有被@FunctionalInterface注释的接口,但是满足@FunctionalInterface注释的约束
@FunctionalInterface注释的约束
- 接口有且只能有个一个抽象方法,只有方法定义,没有方法体
- 在接口中覆写Object类中的public方法,不算是函数式接口的方法。
- 该注解不是必须的,如果一个接口符合"函数式接口"定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。 如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错
@FunctionalInterface简单示例
public static void main(String[] args) {
System.out.println(addstr("a", a -> a + "b"));
System.out.println(method("a", "b", (a, b) -> a + b));
System.out.println(method("梁胖子", "你", (a, b) -> a + "❤" + b));
// JDK8中有双冒号的用法,就是把方法当做参数传过去
Test1 amethod = My::getInstance;
System.out.println(method("a", "b", amethod));
}
@FunctionalInterface
public interface Test2 {
String invoke(String profile);
}
@FunctionalInterface
public interface Test1 {
String invoke(String profile, String document);
}
public static String method(String profile, String document, Test1 a) {
return a.invoke(profile, document);
}
public static String getInstance(String profile, String document) {
return profile + document;
}
public static String addstr(String str, Test2 test2) {
return test2.invoke(str);
}
2.2函数接口
接口名 | 说明 |
Function<T,R> | 接收一个T类型的参数,返回一个R类型的结果 |
Consumer<T> | 接收一个T类型的参数,不返回值 |
Predicate<T> | 接收一个T类型的参数,返回一个boolean类型的结果 |
Supplier<T> | 不接受参数,返回一个T类型的结果 |
BiFunction<T, U, R> | 接收T类型和U类型的两个参数,返回一个R类型的结果 |
BiConsumer<T , U> | 接收T类型和U类型的两个参数,不返回值 |
BiPredicate<T, U> | 接收T类型和U类型的两个参数,返回一个boolean类型的结果 |
public static void main(String[] args) {
// Function<T,R> 接收一个T类型的参数,返回一个R类型的结果
Function<String, String> function = item -> item + item;
// Consumer<T>接收一个T类型的参数,不返回值
Consumer<String> consumer = iterm -> {
System.out.println(iterm);
};
// Predicate<T>接收一个T类型的参数,返回一个boolean类型的结果
Predicate<String> predicate = iterm -> "33".equals(iterm);
// Supplier<T>不接受参数,返回一个T类型的结果
Supplier<String> supplier = () -> new String("");
List<String> list = Arrays.asList("1", "2", "3", "5", "6");
list.stream().map(function).filter(predicate).forEach(consumer);
BiFunction<String, String, String> biFunction = (str1, str2) -> str1 + str2;
BiConsumer<String, String> biConsumer = (str1, str2) -> System.out.println(str1 + str2);
BiPredicate<String, String> biPredicate = (str1, str2) -> str1.length() > str2.length();
Map<String, String> map = new HashMap<>();
map.put("java", "boot");
map.put("spring", "boot");
map.forEach(biConsumer);
System.out.println(biFunction.apply("qunimade ", "zhendifan "));
}
使用default关键字创在interface中直接创建一个default方法,该方法包含了具体的实现代码,说白了就是接口中可以有实现
@FunctionalInterface
public interface BiFunction<T, U, R> {
/**
* Applies this function to the given arguments.
*
* @param t the first function argument
* @param u the second function argument
* @return the function result
*/
R apply(T t, U u);
/**
* Returns a composed function that first applies this function to
* its input, and then applies the {@code after} function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param <V> the type of output of the {@code after} function, and of the
* composed function
* @param after the function to apply after this function is applied
* @return a composed function that first applies this function and then
* applies the {@code after} function
* @throws NullPointerException if after is null
*/
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t, U u) -> after.apply(apply(t, u));
}
}
2.流
2.1定义
stream只能遍历一次
for外部迭代,stream内部迭代
操作分为两种中间操作、终端操作,中间操作会返回一个流,并链接在一起形成一条流水线,但并不会生成任何结果,只有遇到终端操作才处理流水线并返回结果
函数式接口就是只定义一个抽象方法的接口,比如Comparator 和 Runnable 。
lamada基本语法
(parameters) -> expression或者(parameters) -> { statements; } 注意花括号
如果你发现虽然你的业务代码千差万別,但是它们拥有同样的准备和清理阶段,这时,你完全可以将这部分代码用 Lambda实现。
比如文件流处理异常
2.2创建流
1.值创建流
Stream<String> stream = Stream.of("Java 8 ", "Lambdas ", "In ", "Action");
2.数组创建流
int[] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers).sum();
3.由文件创建流
Files.lines(Paths.get("data.txt"), Charset.defaultCharset())){
uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
.distinct()
.count();
4.由函数生成流:创建无限流
Stream.iterate(0, n -> n + 2)
.limit(10)
.forEach(System.out::println);
数列中开始的两个数字是0和1,后续的每个数字都是前两个数字和
Stream.iterate(new int[]{0, 1}, a -> new int[]{a[0]+a[1],a[0]+a[1]+a[1]})
.limit(20)
.forEach(t -> System.out.println("(" + t[0] + "," + t[1] +")"));
5.生成
generate 方法可以按需生成一个无限流,但generate不是依次对每个新生成的值应用函数的,他接收Supplier的lamada新值
Stream.generate(Math::random)
.limit(5)
.forEach(System.out::println);
IntStream.generate(() -> 1).limit(30).forEach(System.out::println);
Stream.generate(new Supplier<String>(){
public String get(){
return "aaa";
}
}).limit(30).forEach(System.out::println);;
2.3中间和终端操作
所有元素和
int sum = numbers.stream().reduce(0, (a, b) -> a + b);
int sum = numbers.stream().reduce(0, Integer::sum);
获取每个元素长度累加和
Integer reduce2 = list.stream().map(a -> a.toString().length()).reduce(0,Integer::sum);
两个集合每个元素相乘 返回一个总集合
List<Integer> numbers1 = Arrays.asList(1, 2, 3);
List<Integer> numbers2 = Arrays.asList(3, 4);
List<String> collect2 = numbers1.stream().flatMap(a -> numbers2.stream().map(b -> a+"*"+b)).collect(Collectors.toList());
int calories = list.stream().map(Integer::byteValue).sum();
int calories = list.stream().mapToInt(Integer::byteValue).sum();
第一个是错误的没有sum方法,必须转化为IntStream才有sum方法,这是因为map返回的是T类型
intStream.boxed()可以将IntStream转化为Stream
int 和 Integer效率差异
IntStream性能优于Stream 避免了自动拆箱等操作
输出1到100的偶数
rangeClosed和range生成1到100的所有数字 rang不包含结尾,rangeClosed包含结尾
IntStream.rangeClosed(1, 100).filter(n -> n % 2 == 0).forEach(System.out::print);
计数、汇总、最大值
menu.stream().collect(Collectors.counting());或者 menu.stream().count();
menu.stream().collect(Collectors.summingInt());
menu.stream().collect(Collectors.maxBy((a,b) -> a-b));
3.实际使用
1.分组计数
Map<Long, Long> collect = bargainInviteRecordDos.stream().collect(Collectors.groupingBy(BargainInviteRecordDo::getSpuId, Collectors.mapping(BargainInviteRecordDo::getSpuId, Collectors.counting())));