Lambda
Lambda 表达式的本质: 函数式接口的实例;
函数式接口:@FunctionalInterface 标识的接口,只有一个抽象方法;
核心函数式接口
接口名称 | 抽象方法 |
---|---|
Consumer | void accept(T t) |
Supplier | T get() |
Predicate | boolean test(T t) |
Function<T,R> | R apply(T) |
Comparator | int compare(T t1, T t2) |
其它常用接口
Function 接口的子接口:
UnaryOperator # T apply(T t);
BiFunction<T,U,R> # R apply(T t , U u);
子接口:BinaryOperator # T apply(T t1 , T t2);
BiConsumer<T,U> # void accept(T t , U u);
BiPredicate<T,U> # boolean test(T t , U u );
方法引用
如果要传递给 Lambda 体的操作,已经有现成的实现方法了,可以使用方法引用;
要求:接口中抽象方法的参数列表和返回值类型,必须与方法引用的参数列表和返回值类型保持一致;
方法引用的格式
- 对象::实例方法名;
例:employeeObj.getName() 与 Supplier 接口中的 T get() —> employeeObj::getName; - 类::静态方法名;
例:Comparator 接口中 int compare(T t1, T t2) 与 Integer中的静态方法:int compare(int x, int y) --> Integer::compare; - 类::实例方法名;
例:BiPredicate 中的 boolean test(T t , U u ) 与 String 类中的 boolean s1.equals(s2); --> String::equals;
Comparator中的 int compare(int a,int b) 与 String 中的 s1.compareTo(s2) --> String::compareTo;
Function 中的 R apply(T) 与 Employee 中的 String empObj.getName(); --> Employee::getName;
来一起看几个例子:
@Test
public void test03(){
//Consumer 中的 void accept(T t)
// PrintStream 中的 void println(T t)
Consumer<String> con1 = str -> System.out.println(str);
con1.accept("test");
PrintStream ps = System.out;
Consumer<String> con2 = ps::println;
con2.accept("test");
}
@Test
public void test04(){
Employee emp = new Employee();
emp.setName("hello");
Supplier<String> sup1 = () -> emp.getName();
System.out.println(sup1.get());
Supplier<String> sup2 = emp::getName;
System.out.println(sup2.get());
}
@Test
public void test05(){
Comparator<Integer> com1 = (i1,i2) -> Integer.compare(i1, i2);
System.out.println(com1.compare(1, 2));
System.out.println("=============>");
// 类::静态方法
Comparator<Integer> com2 = Integer::compare;
System.out.println(com2.compare(2, 1));
}
@Test
public void test06(){
/*
* 类 :: 实例方法
* Comparator 中的 int compare(T t1, T t2)
* String 中的 int t1.compareTo(t2)
* */
Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2);
System.out.println(com1.compare("abc", "abf")); // -3
System.out.println("=============>");
Comparator<String> com2 = String::compareTo;
System.out.println(com2.compare("abc", "abd")); //-1
//匿名子类的形式
Comparator<String> com3 = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
};
}
@Test
public void test07(){
//类::实体方法
// BiPredicate 中的 boolean test(t1,t2)
// String 中的 boolean t1.equals(t2)
BiPredicate<String,String> p1 = (s1,s2) -> s1.equals(s2);
System.out.println(p1.test("ab", "ab"));
BiPredicate<String,String> p2 = String :: equals;
System.out.println(p2.test("ab", "ab"));
}
@Test
public void test08(){
/*类::实体方法
* Function中的 R apply(T t)
* Employee 中的 String empObj.getName()
* */
Employee emp = new Employee("gcx", 18,180000.00);
Function<Employee,String> func1 = e -> e.getName();
System.out.println(func1.apply(emp));
Function<Employee,String> func2 = Employee::getName;
System.out.println(func2.apply(emp));
}
Stream
Stream 对集合数据进行操作,可以执行复杂的查找、过滤和映射;
特点:
Stream 本身无法存储元素;
不改变源对象;
创建 Stream 的四种方式
- Collection 接口中的默认方法 : stream()
List 、Set、Queue等,直接调用 stream() 方法就可以获得 Stream;
Stream stream2 = list.stream(); - Arrays 的静态方法: Arrays.stream(T[] array)
Stream stream1 = Arrays.stream(new String[]{“d”,“e”,“f”}); - Stream 的静态方法 :Stream.of(T…values);
Stream stream = Stream.of(“A”,“B”,“C”,“D”);
stream.forEach(System.out::println); - 基于 Supplier;
Stream s = Stream.generate(Supplier sp);
s.limit(20).forEach(System.out::println);
此种方式创建的 Stream 会不断调用 Supplier.get() 方法来产生下一个元素,打印的时候必须使用 limit() 使其先变成有限序列;
Stream 的优点:Stream几乎不占用空间,因为每个元素都是实时计算出来的,用的时候再算;
支持基本数据类型的流:
Java 的泛型不支持基本数据类型,我们无法使用 Stream 这样的类型,只能使用 Stream , 但是这样会频繁进行拆、装箱,影响效率;
java 标准库提供了 : IntStream LongStream 和 DoubleStream;
// IntStream
IntStream stream = Arrays.stream(new int[]{1,2,3});
//LongStream
List<String> list1 = Arrays.asList("1","2","3");
LongStream longStream = list1.stream().mapToLong(Long::parseLong);
/**
LongStream mapToLong(ToLongFunction<? super T> mapper);
@FunctionalInterface
public interface ToLongFunction<T> {
long applyAsLong(T value);
}
*/
OptionalLong max = list1.stream().mapToLong(Long::parseLong).max();
Long asLong = max.getAsLong();
实例:打印斐波那契数列
@FunctionalInterface
public interface LongSupplier{
long getAsLong();
}
class FibSupplier implements LongSupplier{
public static long num =1 ;
public long getAsLong(){
return fib(num++);
}
public long fib(long num){
if(num ==1 || num == 2){
return 1;
}else{
return fib(num-2) + fib(num-1);
}
}
}
public class Test1{
public static void main(String[] args){
LongStream fib = LongStream.generate(new FibSupplier());
//打印
fib.limit(10).forEach(System.out::println);
}
}
常见的Stream 操作
map()
Stream.map() 是 Stream 最常用的一个转换方法,把一个 Stream 转换为另一个 Stream;
所谓 map 操作,就是把一种运算操作映射到序列的每一个元素上;
map(Function);
Arrays.asList("Apple","Pear","Orange","Banana").stream().map(String::trim).map(String::toLowerCase).forEach(System.out::println);
filter
filter(Predicate)
filter 可以对一个 Stream 的所有元素进行测试,不满足条件就扔掉,剩下的满足条件的元素就构成一个新的Stream;
reduce
reduce(T identity, BinaryOperator accumulator)
T apply(t1,t2) # 负责把上次累加的结果和本次的元素进行运算;
identity # 执行累加计算的初始值;
map 和 filter 都是 Stream 的转换方法,而 Stream.reduce() 是 Stream的一个聚合方法;把所有元素聚成一个结果;
// acc是上次计算的结果,初始为0;
int sum = Stream.of(1,2,3,4,5,6).reduce(0,(acc,n) -> acc + n );
如果去掉初始值,我们会得到一个 Optional:
Optional<Integer> opt = stream.reduce((acc,n) -> acc +n);
if(opt.isPresent()){
System.out.println(opt.get());
}
因为 Stream 的元素有可能是 0 个,这样就没法调用 reduce() 的聚合函数了;
注意:
Stream 的转换操作 map 和 filter 并不会触发任何计算(只保存了转换规则);
聚合操作是真正需要从 Stream 请求数据的,会立刻促使 Stream 输出它的每一个元素;
输出为List
如果我们希望把 Stream 的元素保存到集合(把Stream变成list是一个聚合操作);
// 获得的实体类是 ArrayList
List<String> collect = stream.filter(s -> s!=null && !s.isEmpty()).collect(Collectors.toList());
输出为数组
A[] toArray(IntFunction<A[]> generator);
@FunctionalInterface
public interface IntFunction{
R apply(int value);
}
String[] strings = list.stream().toArray(String[]::new);
输出为 Map
Map<String,String> map = stream.collect(Collectors**.toMap(**
s -> s.substring(0,s.indexOf(":")),
s -> s.substring(s.indexOf(":") +1)
));
toMap 里面是两个 Function 实现类,分别对应 key的映射,和 value 的映射;
分组输出
List<String> list1 = Arrays.asList("Apple","Banana","Blackberry","Coconut", "Avocado","Cherry","Apricots");
//以单词的首字母分组;
Map<String,List<String>> collect1 = list1.stream().collect(Collectors.groupingBy(s -> s.substring(0,1),Collectors.toList()));
排序
// 要求每个元素必须实现 Comparable 接口;
stream.sorted().collect(Collectors.toList());
//传入比较规则;
stream.sorted(String::compareToIgnoreCase).collect(Collectors.toList());
去重
List<String> list2 = Arrays.asList("a","A","b","b","A");
List<String> collect2 = list2.stream().distinct().collect(Collectors.toList());
截取
//skip(2): 跳过前两个;
// limit(3) : 输出 3个;
List<String> collect2 = list2.stream().skip(2).limit(3).collect(Collectors.toList());
并行
将对 Stream 元素的单线程处理,变为并行处理:
stream.parallel().sorted().toArray(String[] :: new );
合并
Stream.comcat(s1,s2);
遍历
forEach(Consumer)
它可以循环处理 Stream 的每个元素,我们经常传入 System.out::println 来打印 Stream的元素;
flatMap
所谓 flatMap() 是把 Stream 的每个元素 (list) 映射为 Stream, 然后合并成一个新的 Stream
Stream<List<Integer>> s = Stream.of(
Arrays.asList(1,2,3),
Arrays.asList(4,5,6),
Arrays.asList(7,8,9)
);
Stream<Integer> i = s.flatMap(list -> list.stream());
最大、最小
max(Comparator<? super T> cp ) : 找出最大元素;
min(Comparator<? super T> cp ) : 找出最小元素;
测试 Stream 的元素是否满足条件
boolean allMatch(Predicate<? super T>) : 测试是否所有元素均满足测试条件;
boolean anyMatch(Predicate<? super T>) : 测试是否至少有一个元素满足测试条件。
sum、average
针对IntStream 、 LongStream、 DoubleStream , 还额外提供了以下聚合方法:
sum() : 对所有元素求和;
average() : 对所有元素求平均数;
拼接流中的元素
//用 xxx 拼接流中的元素;
String s = list2.stream().collect(Collectors.joining("xxx"));
findFirst、orElse、orElseThrow
findFirst: 获取第一个元素,返回值是 Optional
orElse : 通常与 findFirst 配合使用,意思是,找不到就返回指定的默认值;
orElseThrow : 通常与 findFirst 配合使用,意思是,找不到就抛异常;
List<String> list2 = Arrays.asList("r","ba","c","sd","e","f");
String s1 = list2.stream().filter(s -> s.contains("a")).findFirst().orElse("haha");
Optional<String> a1 = list2.stream().filter(s -> s.contains("a")).findFirst();
String a1 = list2.stream().filter(s->s.contains("a")).findFirst().orElseThrow(RuntimeException::new);