Java学习之Stream流、方法引用(Lambda)

1. Stream流

1.1概述

说到Stream便容易想到I/O Stream,而实际上,谁规定“流”就一定是“IO流”呢?在Java 8中,得益于Lambda所带来的函数式编程,引入了一个全新的Stream概念,用于解决已有集合类库既有的弊端。

Stream是Java8最新加入的接口,Stream(流)是一个来自数据源的元素队列元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。数据源 流的来源。 可以是集合,数组 等。

  • stream特点:

    • Stream流属于管道流,只能使用一次,第一个stream流使用完毕,数据就会转移到下一个stream流上
    • 而第一个stream流使用完毕就会关闭,所以第一个流就不能再调用方法了
  • Stream的基础特征:

    • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道,如同流式风格(fluent style)。 这样做可以对操作进行优化,比如延迟执行(laziness)和短路( short-circuiting)。

    • 内部迭代: 以前对集合遍历都是通过Iterator或者增强for的方式, 显式的在集合外部进行迭代, 这叫做外部迭 代。 Stream提供了内部迭代的方式,流可以直接调用遍历方法。

使用一个流的时候,通常包括三个基本步骤:获取一个数据源(source)→ 数据转换→执行操作获取想要的结 果,每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以有多次转换), 这就允许对其操作可以 像链条一样排列,变成一个管道。

1.2 集合获取Stream的方式

在这里插入图片描述

1.2.1 Collection(单列集合)都可以通过stream取得默认的流

//单列集合获取流(Collection),所有单列集合获取Stream流的方式都一样(如 List,Set等)
        Collection<String> collection = new ArrayList<String>();
        //直接Collection中的stream方法来获取流
        Stream<String> stream = collection.stream();

        //双列集合怎么获取流
        // 首先我们需要先把双列集合转换成为单列集合
        Map<String, String> map = new HashMap<>();
        //1.第一种是获取双列集合的key集合,然后在获取Stream流
        Set<String> keySet = map.keySet();
        Stream<String> stream1 = keySet.stream();

        //2.通过获取双列集合的值集合,然后在获取stream
        Collection<String> values = map.values();
        Stream<String> stream2 = values.stream();

        //获取双列集合的键值对结合
        Set<Map.Entry<String, String>> entries = map.entrySet();
        Stream<Map.Entry<String,String>> stream3 = entries.stream();

1.2.2 数组获取Stream的流

是直接通过Stream接口的of()静态方法 获取Stream流, 因为Stream.of(…)的参数是一个可变参数, 底层就是数组,所以我们直接传递数组可以,或则直接传递参数

//数组是如何获取Stream流的
        String[] strArray = {"sda","sa","ssss"};
        //1. 通过Stream接口的of()静态方法 获取Stream流
        Stream<String> stream4 = Stream.of(strArray);
        Stream<String> stream5 = Stream.of("123","234","345");

1.3 Stream的一些常用方法

  • 流模型的操作很丰富,这里介绍一些常用的API。这些方法可以被分成两种:
    • 延迟方法:返回值类型仍然是 Stream 接口自身类型的方法,因此支持链式调用。(除了终结方法外,其余方 法均为延迟方法。)

    • 终结方法:返回值类型不再是 Stream 接口自身类型的方法,因此不再支持类似 StringBuilder 那样的链式调 用。本小节中,终结方法包括 count 和 forEach 方法。

实例代码:

public class StreamDemo2 {
    public static void main(String[] args) {
        ArrayList<String> nameArrayList = new ArrayList<>();
        nameArrayList.add("张三");
        nameArrayList.add("李四");
        nameArrayList.add("王五");
        nameArrayList.add("赵六");

        //streamTestForEach(nameArrayList);
        //streamTestFilter(nameArrayList,"王五");

        //把字符串集合 映射成Integer类型的集合
        ArrayList<String> arrayList2 = new ArrayList<>();
        arrayList2.add("123");
        arrayList2.add("456");
        arrayList2.add("789");
        //streamTestMap(arrayList2);

        //使用stream流来获取集合的长度
        //System.out.println("集合的个数======" + streamTestCount(arrayList2));

        //获取集合的前几个元素
        //streamTestLimit(nameArrayList);

        //跳过集合的前面几个元素
        //streamTestSkip(nameArrayList);

        //使用stream把两个集合组合起来(相同类型的)
        streamTestConcat(nameArrayList,arrayList2);
    }
    /*
        测试Stream中的forEach方法
        forEach: 该方法属于一个终结方法,就调用该方法之后,得到了结果在无法调用其他方法了
            虽然方法名字叫 forEach ,但是与for循环中的“for-each”昵称不同。
            该方法接收一个 Consumer 接口函数(消费型接口),会将每一个流元素交给该函数进行处理。

        使用Stream来遍历集合
     */
    public static void streamTestForEach(ArrayList<String> arrayList){
        arrayList.stream().forEach(s -> System.out.println("name====" + s));
    }

    /*
        使用Stream流来 去掉集合中的某个元素 并遍历集合
        filter()筛选方法
        可以通过 filter 方法将一个流转换成另一个子集流,该接口接收一个 Predicate 函数式接口参数(可以是一个Lambda或方法引用)作为筛选条件。
     */
    public static void streamTestFilter(ArrayList<String> arrayList, String name){
        arrayList.stream().filter(s -> !s.equals(name)).forEach(s -> System.out.println("name====" + s));
    }

    /*
        map:如果需要将流中的元素映射到另一个流中,可以使用 map 方法。方法签名:
        该接口需要一个 Function 函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。
        映射

        例子: 把string类型的集合转换成 Integer类型的集合
     */
    public static void streamTestMap(ArrayList<String> arrayList) {
        arrayList.stream().map(str -> Integer.parseInt(str)).forEach(s -> System.out.println(s));
    }

    /*
        使用stream来获取集合的长度,正如旧集合 Collection 当中的 size 方法一样,流提供 count 方法来数一数其中的元素个数:
        long count();
        该方法返回一个long值代表元素个数(不再像旧集合那样是int值)
        该放放也是一个终结方法
     */
    public static long streamTestCount(ArrayList<String> arrayList) {
        return arrayList.stream().count();
    }

    /*
        Stream<T> limit(long maxSize); : 方法可以对流进行截取,只取用前n个。
        参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作
     */
    public static void streamTestLimit(ArrayList<String> arrayList) {
        arrayList.stream().limit(arrayList.size()-1).forEach(str -> System.out.println(str));
    }

    /*
        Stream<T> skip(long n):如果希望跳过前几个元素,可以使用 skip 方法获取一个截取之后的新流:
                                如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流.
     */
    public static void streamTestSkip(ArrayList<String> arrayList) {
        arrayList.stream().skip(arrayList.size()-1).forEach(str -> System.out.println(str));
    }

    /*
        static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
        如果有两个流,希望合并成为一个流,那么可以使用 Stream 接口的静态方法 concat :
        备注:这是一个静态方法,与 java.lang.String 当中的 concat 方法是不同的。
     */
    public static void streamTestConcat(ArrayList<String> arrayList, ArrayList<String> arrayList2) {
        Stream.concat(arrayList.stream(), arrayList2.stream()).forEach(str -> System.out.println(str));
    }
}

2.方法引用

2.1 概述

  • 方法引用:使用方法引用优化Lambda表示式的前提
    • 1.对象是已经存在的
    • 2.成员方法也是已经存在的

只有满足这个两个条件,我们才可以使用方法引用优化Lambda表达式

  • :: : 是方法引用的引用符 而它所在的表达式被称为方法引用

  • 示例:


/*
 * 定义一个函数式接口
 */
public interface Printable {
    //打印字符串的抽象方法
    public abstract void print(String s);
}


public class MethodDemo {
    public static void main(String[] args) {
        //调用方法,由于是一个函数式接口,所以可以使用Lambda表达式
        testPrintable(str -> System.out.println(str));

        /*
            分析:
                Lambda表达式的目的,打印参数传递的字符串
                把参数str传递给 System.out对象,调用out对象中的println()方法进行了输出
                注意:
                    1.System.out对象已经存在的
                    2.println()方法是已近存在的
                所以我们可以使用方法引用来优化Lambda表达式
                可以使用System.out直接应用println()方法

                格式:
                    对象名::方法名
                    :: : 是方法引用的引用符 而它所在的表达式被称为方法引用
                    如果Lambda要表达的函数方案已经存在于某个方 法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的替代者。

                Lambda表达式:testPrintable(str -> System.out.println(str));
                方法引用写法:testPrintable(System.out::println);
                以上两种写法完全等效

                注意: Lambda表达式中传递的参数一定是方法引用中的那个方法可以接受的类型,否则会抛出异常

                在这里就是 str的参数类型 一定是println方法中可以接受的数据类型
         */
        //使用方法引用优化Lambda表达式
        testPrintable(System.out::println);
   }

    public static void testPrintable(Printable printable) {
        printable.print("HelloWorld");
    }
}

2.2 通过对象引用成员方法

/*
 * 定义一个函数式接口
 */
public interface Printable {
    //打印字符串的抽象方法
    public abstract void print(String s);
}

public class PrintDemo {
    //打印字符串方法
    public void print(String str) {
        System.out.println(str.toUpperCase());
    }
}


public class MethodDemo {
    public static void main(String[] args) {

        //调用printString,方法参数是一个函数式接口
        printString(s -> {
            //创建对象,然后调用对象的方法打印字符串
            PrintDemo obj = new PrintDemo();
            obj.print(s);
        });

        //使用方法引用来优化Lambda表达式 对象名::方法名
        //对象存在 PrintDemo obj = new PrintDemo();
        //成员方法也存在 ,所以符合方法引用的条件
        PrintDemo obj = new PrintDemo();
        printString(obj::print);
    }

    public static void printString(Printable printable){
        printable.print("Hello");
    }
}

2.3 通过类名引用成员方法

@FunctionalInterface
public interface Calcuble {
    /*
     *   计算一个值的绝对值
     */
    public abstract int calculateABS(int a);
}

public class MethodDemo {
    public static void main(String[] args) {
        //使用Lambda表达式来写
        int index = method(-10, count -> Math.abs(count));
        System.out.println("index======" + index);

        /*
            使用静态方法优化Lambda表达式
            Math类是存在的
            abs计算绝对值的方法是存在

            所以我们可以使用类名引用静态方法来优化
            格式:
                类名::静态方法名
         */
        int index2 = method(-20,Math::abs);
        System.out.println("方法引用优化: index2====" + index2);
    }

    public static int method(int a, Calcuble calcuble){
        //调用接口中的方法 计算绝对值
        return calcuble.calculateABS(a);
    }
}

2.4 通过super引用成员方法


@FunctionalInterface
public interface Greetable {
    public abstract void greet();
}


/*
    定义父类
 */
public class Human {
    public void sayHello(){
        System.out.println("Hello 我是Human");
    }
}


/*
    定义子类
 */
public class Man extends Human {
    @Override
    public void sayHello() {
        System.out.println("Hello,我是man");
    }

    public void show() {
        //调用方法
        method(() -> {
            Human human = new Human();
            human.sayHello();
        });

        method(() -> {
            //因为是父子关系,所以我们可以通过关键字super代表父类,直接调用父类的方法
            super.sayHello();
        });

         /*
                如何使用super引用类成员的方法来优化
                super是已经存在的
                父类的成员方法是已经存在的
                所以我们可以使用super引用类的成员方法优化
                格式:
                    super::类的成员方法名称
             */
        method(super::sayHello);
    }

    public void method(Greetable greetable){
        greetable.greet();
    }


    public static void main(String[] args) {
        new Man().show();
    }
}

2.5 通过this引用成员方法

public interface MyInterface {
    public abstract void sayHello();
}

public class MethodDemo {
    public static void main(String[] args) {
        new MethodDemo().show();
    }

    public static void test(MyInterface myInterface){
        myInterface.sayHello();
    }
    public void show(){
        //一般调用方法
        test(() -> {
            MethodDemo methodDemo = new MethodDemo();
            methodDemo.buy();
        });

        //我们可以通过this调用当前的方法
        test(() -> {
            this.buy();
        });

        /*
        我们可以通过this关键字使用this引用类的成员方法的方式来优化Lambda
        格式:
            this::类的成员方法名
         */
        test(this::buy);
    }


    public void buy() {
        System.out.println("想买什么买什么");
    }
}

2.6 类的构造器引用

public interface PersonBuilder {
    //根据传入的名称,返回一个person对象
    public abstract Person builder(String name);
}

public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }


}

public class MethodDemo {
    public static void main(String[] args) {
        //调用方法-> 使用Lambda表达式调用
        printPerson("张三",name -> new Person(name));

        //使用方法引用——类的构造器方法引用
        //格式: 类名::new
        printPerson("李四",Person::new);
    }

    public static void printPerson(String name,PersonBuilder personBuilder) {
        Person person = personBuilder.builder(name);
        System.out.println(person.getName());
    }
}

2.7 数组的构造器的引用

public interface ArrayBuilder {
    //根据数组的长度创建一个数据
    public abstract int[] builder(int length);
}

public class MethodDemo {
    public static void main(String[] args) {
        //Lambda表达式
        int[] temp = creatArray(10,s -> new int[s]);
        System.out.println(temp.length);
        /*
            已知创建的是int[] 数组
            然后数组的长度已知
            可以使用方法引用来优化Lambda表达式
            格式:
                int[](数组类型)::new
         */
        int[] temp1 = creatArray(20,int[]::new);
        System.out.println(temp1.length);
    }

    public static int[] creatArray(int length, ArrayBuilder arrayBuilder) {
        return  arrayBuilder.builder(length);
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值