Lambda & Stream

Lambda

Lambda 表达式的本质: 函数式接口的实例
函数式接口:@FunctionalInterface 标识的接口,只有一个抽象方法

核心函数式接口

接口名称抽象方法
Consumervoid accept(T t)
SupplierT get()
Predicateboolean test(T t)
Function<T,R>R apply(T)
Comparatorint 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 体的操作,已经有现成的实现方法了,可以使用方法引用;

要求:接口中抽象方法的参数列表和返回值类型,必须与方法引用的参数列表和返回值类型保持一致;

方法引用的格式

  1. 对象::实例方法名;
    例:employeeObj.getName() 与 Supplier 接口中的 T get() —> employeeObj::getName;
  2. 类::静态方法名;
    例:Comparator 接口中 int compare(T t1, T t2) 与 Integer中的静态方法:int compare(int x, int y) --> Integer::compare;
  3. 类::实例方法名;
    例: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 的四种方式

  1. Collection 接口中的默认方法 : stream()
    List 、Set、Queue等,直接调用 stream() 方法就可以获得 Stream;
    Stream stream2 = list.stream();
  2. Arrays 的静态方法: Arrays.stream(T[] array)
    Stream stream1 = Arrays.stream(new String[]{“d”,“e”,“f”});
  3. Stream 的静态方法 :Stream.of(T…values);
    Stream stream = Stream.of(“A”,“B”,“C”,“D”);
    ​ stream.forEach(System.out::println);
  4. 基于 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);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值