大厂必备Stream流、方法引用的使用

文章详细介绍了Java8中新增的Stream流的使用,包括获取流的方式、常用方法如forEach、filter、count、limit、skip等,并通过实例演示了流的链式操作。此外,还讲解了方法引用的概念和不同类型的引用方式,如成员方法引用、静态方法引用、类构造引用和数组构造引用,强调了方法引用作为Lambda表达式的简化形式。
摘要由CSDN通过智能技术生成

大厂必备Stream流、方法引用的使用

1 Stream的基本使用

1.1 思想

当需要对多个元素进行操作(特别是多步操作)的时候,考虑到性能及便利性,我们应该首先拼好一个“模型”步骤方案,然后再按照方案去执行它。【流水线】

在这里插入图片描述

这张图中展示了过滤、映射、跳过、计数等多步操作,这是一种集合元素的处理方案,而方案就是一种“函数模型”。图中的每一个方框都是一个“流”,调用指定的方法,可以从一个流模型转换为另一个流模型。而最右侧的数字3是最终结果。

注意:“Stream流”其实是一个集合元素的函数模型,它并不是集合,也不是数据结构,其本身并不存储任何元素(或其地址值)。

1.2 获取流的方式

java.util.stream.Stream是Java 8新加入的最常用的流接口。(这并不是一个函数式接口。)

①根据Collection

java.util.Collection接口中加入了default方法stream用来获取流,所以其所有实现类均可获取流。

@Test
public void test(){
    List<String> list = new ArrayList<>();
    // ...
    Stream<String> stream1 = list.stream();

    Set<String> set = new HashSet<>();
    // ...
    Stream<String> stream2 = set.stream();
}
②根据数组获取(Stream.of)

如果使用的不是集合或映射而是数组,由于数组对象不可能添加默认方法,所以Stream接口中提供了静态方法of
of方法的参数其实是一个可变参数,所以支持数组。

@Test
public void test(){
    Integer[] arr = {1,2,3};
    Stream<Integer> stream = Stream.of(arr);
    //Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5)
}

1.3 Stream流的常用方法

流模型的操作很丰富,这里介绍一些常用的API。这些方法可以被分成两种:

  • 终结方法:返回值类型不再是Stream接口自身类型的方法,因此不再支持类似StringBuilder那样的链式调用。本小节中,终结方法包括count和forEach方法。【返回值不是stream,不支持继续链式调用】
  • 非终结方法:返回值类型仍然是Stream接口自身类型的方法,因此支持链式调用。(除了终结方法外,其余方法均为非终结方法。)
①forEach

虽然方法名字叫forEach,但是与for循环中的“for-each”昵称不同,该方法并不保证元素的逐一消费动作在流中是被有序执行的。【非终结方法】

@Test
public void test(){
    Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
    stream.forEach(i -> System.out.println(i));
}

在这里,lambda表达式(Integer i)->{System.out.println(i);}就是一个Consumer函数式接口的示例。

②filter

可以通过filter方法将一个流转换成另一个子集流【非终结】

@Test
public void test(){
    Stream<Integer> original = Stream.of(1, 2, 3, 4, 5);
    Stream<Integer> result = original.filter(i -> {
        return i > 3;
    });
    result.forEach(num -> {System.out.println(num);});//4,5
}
③count

统计流中元素的个数,类似于Collection中的size()【终结方法】

 @Test
 public void test(){
     Stream<Integer> original = Stream.of(1, 2, 3, 4, 5);
     Stream<Integer> result = original.filter(i -> {
         return i > 3;
     });
     System.out.println(result.count());//2
 }
④limit

limit方法可以对流进行截取,只取用前n个。【非终结】
参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作。

@Test
public void test() {
    Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
    Stream<Integer> result = stream.limit(2);
    //1 2
    result.forEach(i -> {
        System.out.print(i+" ");
    });
}
⑤skip

如果希望跳过前几个元素,可以使用skip方法获取一个截取之后的新流:【非终结】

@Test
public void test() {
    Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
    Stream<Integer> result = stream.skip(2);
    //3 4 5 
    result.forEach(i -> {
        System.out.print(i+" ");
    });
}
⑥concat

如果有两个流,希望合并成为一个流,那么可以使用Stream接口的静态方法concat【非终结】

@Test
public void test() {
    Stream<Integer> stream1 = Stream.of(1, 2, 3);
    Stream<Integer> stream2 = Stream.of(4, 5, 6);
    Stream<Integer> result = Stream.concat(stream1, stream2);
    result.forEach(i -> {
        //1 2 3 4 5 6 
        System.out.print(i + " ");
    });
}
综合运用
@Test
public void test() {
    Stream<Integer> stream1 = Stream.of(1, 2, 3);
    Stream<Integer> stream2 = Stream.of(4, 5, 6);
    //1,2,3,4,5,6 -> 2,3,4,5,6[skip] -> 2,3,4,5[filter] -> 2,3,4[limit]
    Stream.concat(stream1, stream2).skip(1).filter(i -> {
        return i <= 5;
    }).limit(3).forEach(num -> {System.out.print(num + " ");});

}

2 方法引用

双冒号为方法引用运算符,而它所在的表达式被称为方法引用。

2.1 概述

来看一个简单的函数式接口以应用Lambda表达式 , 在accept方法中接收字符串 , 目的就是为了打印显示字符串 , 那么通过Lambda来使用它的代码很简单:

public class DemoPrintSimple {
    private static void printString(Consumer<String> data, String str) {
        data.accept(str);
    }
    public static void main(String[] args) {
      	printString(s -> System.out.println(s), "Hello World");
    }
}

由于lambda表达式中,调用了已经实现的println方法 ,可以使用方法引用替代lambda表达式.
如果Lambda要表达的函数方案 , 已经存在于某个方法的实现中,那么则可以使用方法引用。

如上例中,System.out对象中有个println(String)方法 , 恰好就是我们所需要的 , 那么对于Consumer接口作为参数,对比下面两种写法,完全等效:

  • Lambda表达式写法:s -> System.out.println(s);
    拿到参数之后经Lambda之手,继而传递给System.out.println方法去处理。
  • 方法引用写法:System.out::println
    直接让System.out中的println方法来取代Lambda。
@Test
public void test4(){
    List<Integer> list = new ArrayList<>();
    list.add(2);
    list.add(1);
    list.add(3);
    list.stream().forEach(System.out::println);
}

2.2 引用方式

①引用成员方法(str::toUpperCase)
@Test
public void test4(){
    String str = "fas";
    printUp(str::toUpperCase);
}

public void printUp(Supplier<String> supplier){
    System.out.println(supplier.get());
}
②引用静态方法(Math::random)
@Test
public void test4(){
    getNum(Math::random);
}

public void getNum(Supplier<Double> supplier){
    System.out.println(supplier.get());
}
③类构造引用

Function<String, Cat>中String是source,Cat是target

@Test
public void test4(){
    Cat cat = getCat(Cat::new, "tom");
    System.out.println(cat);
}

public Cat getCat(Function<String, Cat> function, String name){
    Cat cat = function.apply(name);
    return cat;
}
④数组构造引用
@Test
public void test4(){
    int[] arr = getArr(int[]::new, 1);
    System.out.println(arr);
}

public int[] getArr(Function<Integer, int[]> function, Integer num){
    int[] arr = function.apply(num);
    return arr;
}

注意 : 方法引用是对Lambda表达式符合特定情况下的一种缩写,它使得我们的Lambda表达式更加的精简,也可以理解为Lambda表达式的缩写形式
参考文章:
https://juejin.cn/post/6896614244385554446

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值