• Stream流 方法引用


    1、Stream流

    得益于Lambda所带来的函数式编程,引入了一个全新的Stream概念,用于解决已有集合类库既有的弊端。
    Java 8的Lambda让我们可以更加专注于做什么(What),而不是怎么做(How)

    1.1. 流式思想

    整体来看,流式思想类似于工厂车间的“生产流水线”。
    之前:
    
    import java.util.ArrayList;
    import java.util.List;
    public class Demo02NormalFilter {
        public static void main(String[] args) {
            List<String> list = new ArrayList<>();
            list.add("张无忌");
            list.add("周芷若");
            list.add("赵敏");
            list.add("张强");
           list.add("张三丰");
           
           List<String> zhangList = new ArrayList<>();
            for (String name : list) {
                if (name.startsWith("张")) {
                    zhangList.add(name);
                }
            }
            
            List<String> shortList = new ArrayList<>();
            for (String name : zhangList) {
                if (name.length() == 3) {
                    shortList.add(name);
                }
            }
    
            for (String name : shortList) {
                System.out.println(name);
            }
        }
    }
    
    
    Stream的更优写法
    
    import java.util.ArrayList;
    import java.util.List;
    public class Demo03StreamFilter {
        public static void main(String[] args) {
            List<String> list = new ArrayList<>();
            list.add("张无忌");
            list.add("周芷若");
            list.add("赵敏");
            list.add("张强");
           list.add("张三丰");
            
            list.stream()
                .filter(s ‐> s.startsWith("张"))
                .filter(s ‐> s.length() == 3)
                .forEach(System.out::println);
        }
    }

    1.2. 获取流

    根据Collection获取流

    import java.util.*;
    import java.util.stream.Stream;
    public class Demo04GetStream {
        public static void main(String[] args) {
            List<String> list = new ArrayList<>();
            // ...
            Stream<String> stream1 = list.stream();
            Set<String> set = new HashSet<>();
            // ...
            Stream<String> stream2 = set.stream();
            Vector<String> vector = new Vector<>();
            // ... 
            Stream<String> stream3 = vector.stream();
        }
    }

    根据Map获取流

    import java.util.HashMap;
    import java.util.Map;
    import java.util.stream.Stream;
    public class Demo05GetStream {
        public static void main(String[] args) {
            Map<String, String> map = new HashMap<>();
            // ...
            Stream<String> keyStream = map.keySet().stream();
            Stream<String> valueStream = map.values().stream();
           Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
        }
    }

    根据数组获取流

    import java.util.stream.Stream;
    public class Demo06GetStream {
        public static void main(String[] args) {
            String[] array = { "张无忌", "张翠山", "张三丰", "张一元" };
            Stream<String> stream = Stream.of(array);
        }
    }

    1.3. 常用方法

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

    逐一处理:forEach

    void forEach(Consumer<? super T> action);
    java.util.function.Consumer<T>接口是一个消费型接口。
    Consumer接口中包含抽象方法void accept(T t),意为消费一个指定泛型的数据。
    
    import java.util.stream.Stream;
    public class Demo12StreamForEach {
        public static void main(String[] args) {
            Stream<String> stream = Stream.of("张无忌", "张三丰", "周芷若");
            stream.forEach(name‐> System.out.println(name));
        }
    }

    过滤:fifilter

    Stream<T> filter(Predicate<? super T> predicate);
    boolean test(T t);
    
    import java.util.stream.Stream;
    public class Demo07StreamFilter {
        public static void main(String[] args) {
            Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
            Stream<String> result = original.filter(s ‐> s.startsWith("张"));
        }
    }

    映射:map

    这段代码中, map 方法的参数通过方法引用,将字符串类型转换成为了int类型(并自动装箱为 Integer 类对象)
    <R> Stream<R> map(Function<? super T, ? extends R> mapper);
    R apply(T t);
    
    import java.util.stream.Stream;
    public class Demo08StreamMap {
        public static void main(String[] args) {
            Stream<String> original = Stream.of("10", "12", "18");
            Stream<Integer> result = original.map(str‐>Integer.parseInt(str));
        }
    }

    统计个数:count

    long count();
    
    import java.util.stream.Stream;
    public class Demo09StreamCount {
        public static void main(String[] args) {
            Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
            Stream<String> result = original.filter(s ‐> s.startsWith("张"));
            System.out.println(result.count()); // 2
        }
    }

    取用前几个:limit

    Stream<T> limit(long maxSize);
    
    import java.util.stream.Stream;
    public class Demo10StreamLimit {
        public static void main(String[] args) {
            Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
            Stream<String> result = original.limit(2);
            System.out.println(result.count()); // 2
        }
    }

    跳过前几个:skip

    Stream<T> skip(long n);
    
    import java.util.stream.Stream;
    public class Demo11StreamSkip {
        public static void main(String[] args) {
            Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
            Stream<String> result = original.skip(2);
            System.out.println(result.count()); // 1
        }
    }

    组合:concat

    static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
    
    import java.util.stream.Stream;
    public class Demo12StreamConcat {
        public static void main(String[] args) {
            Stream<String> streamA = Stream.of("张无忌");
            Stream<String> streamB = Stream.of("张翠山");
            Stream<String> result = Stream.concat(streamA, streamB);
        }
    }

    1.4. 练习:集合元素处理(传统方式)

    现在有两个 ArrayList 集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以
    下若干操作步骤:
    1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
    2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。
    3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
    4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。
    5. 将两个队伍合并为一个队伍;存储到一个新集合中。
    6. 根据姓名创建 Person 对象;存储到一个新集合中。
    7. 打印整个队伍的Person对象信息。
    public class Person {
        private String name;
        public Person() {}
        public Person(String name) {
            this.name = name;
        }
        @Override
        public String toString() {
            return "Person{name='" + name + "'}";
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }
    
    
    import java.util.ArrayList;
    import java.util.List;
    public class DemoArrayListNames {
        public static void main(String[] args) {
            //第一支队伍
            ArrayList<String> one = new ArrayList<>();
            one.add("迪丽热巴");
            one.add("宋远桥");
            one.add("苏星河");
            one.add("石破天");
            one.add("石中玉");
            one.add("老子");
            one.add("庄子");
            one.add("洪七公");
            //第二支队伍
            ArrayList<String> two = new ArrayList<>();
            two.add("古力娜扎");
            two.add("张无忌");
            two.add("赵丽颖");
            two.add("张三丰");
            two.add("尼古拉斯赵四");
            two.add("张天爱");
            two.add("张二狗");
            // ....
        }
    }
    
    public class DemoArrayListNames {
        public static void main(String[] args) {
            List<String> one = new ArrayList<>();
            // ...
            
            List<String> two = new ArrayList<>();
            // ...
            
            // 第一个队伍只要名字为3个字的成员姓名;
            List<String> oneA = new ArrayList<>();
            for (String name : one) {
                if (name.length() == 3) {
                    oneA.add(name);
                }
            }
            
            // 第一个队伍筛选之后只要前3个人;
            List<String> oneB = new ArrayList<>();
                for (int i = 0; i < 3; i++) {
                    oneB.add(oneA.get(i));
            }
            
            // 第二个队伍只要姓张的成员姓名;
            List<String> twoA = new ArrayList<>();
                for (String name : two) {
                    if (name.startsWith("张")) {
                        twoA.add(name);
                    }
            }
            
            // 第二个队伍筛选之后不要前2个人;
            List<String> twoB = new ArrayList<>();
                for (int i = 2; i < twoA.size(); i++) {
                    twoB.add(twoA.get(i));
            }
            
            // 将两个队伍合并为一个队伍;
            List<String> totalNames = new ArrayList<>();
            totalNames.addAll(oneB);
            totalNames.addAll(twoB);
          
              // 根据姓名创建Person对象;
            List<Person> totalPersonList = new ArrayList<>();
            for (String name : totalNames) {
                totalPersonList.add(new Person(name));
            }
            // 打印整个队伍的Person对象信息。
            for (Person person : totalPersonList) {
                System.out.println(person);
            }
        }
    }
    
    运行结果为:
    Person{name='宋远桥'}
    Person{name='苏星河'}
    Person{name='石破天'}
    Person{name='张天爱'}
    Person{name='张二狗'}

    1.5. 练习:集合元素处理(Stream方式)

    等效的Stream流式处理代码为:
    import java.util.ArrayList;
    import java.util.List;
    import java.util.stream.Stream;
    public class DemoStreamNames {
        public static void main(String[] args) {
            List<String> one = new ArrayList<>();
            // ...
            
            List<String> two = new ArrayList<>();
            // ...
           
             // 第一个队伍只要名字为3个字的成员姓名;
            // 第一个队伍筛选之后只要前3个人;
            Stream<String> streamOne = one.stream().filter(s ‐> s.length() == 3).limit(3);
            
            // 第二个队伍只要姓张的成员姓名;
            // 第二个队伍筛选之后不要前2个人;
            Stream<String> streamTwo = two.stream().filter(s ‐> s.startsWith("张")).skip(2);
            
            // 将两个队伍合并为一个队伍;
            // 根据姓名创建Person对象;
            // 打印整个队伍的Person对象信息。
            Stream.concat(streamOne, streamTwo).map(Person::new).forEach(System.out::println);
        }
    }
    
    运行效果完全一样:
    Person{name='宋远桥'}
    Person{name='苏星河'}
    Person{name='石破天'}
    Person{name='张天爱'}
    Person{name='张二狗'}

    2、方法引用

    2.1. Lambda场景

    这段代码的问题在于,对字符串进行控制台打印输出的操作方案,明明已经有了现成的实现,那就是 System.out
    对象中的 println(String) 方法。既然Lambda希望做的事情就是调用 println(String) 方法,那何必自己手动调用呢?
    @FunctionalInterface
    public interface Printable {
        void print(String str);
    }
    
    public class Demo01PrintSimple {
        private static void printString(Printable data) {
            data.print("Hello, World!");
        }
        public static void main(String[] args) {
            printString(s ‐> System.out.println(s));
        }
    }

    2.2. 方法引用:改进

    双冒号 :: 为引用运算符,而它所在的表达式被称为方法引用。如果Lambda要表达的函数方案已经存在于某个方法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的替代者。
     
    Lambda表达式写法: s -> System.out.println(s);
    方法引用写法: System.out::println

    方法引用符

    public class Demo02PrintRef {
        private static void printString(Printable data) {
            data.print("Hello, World!");
        }
        public static void main(String[] args) {
            printString(System.out::println);
        }
    }
    
    请注意其中的双冒号 :: 写法,这被称为“方法引用”,而双冒号是一种新的语法。

    通过对象名引用成员方法

    public class MethodRefObject {
        public void printUpperCase(String str) {
            System.out.println(str.toUpperCase());
        }
    }
    
    @FunctionalInterface
    public interface Printable {
        void print(String str);
    }
    
    public class Demo04MethodRef {
        private static void printString(Printable lambda) {
            lambda.print("Hello");
        }
        public static void main(String[] args) {
            MethodRefObject obj = new MethodRefObject();
            printString(obj::printUpperCase);
        }
    }

    通过类名称引用静态方法

    @FunctionalInterface
    public interface Calcable {
        int calc(int num);
    }
    
    第一种写法是使用Lambda表达式:
    public class Demo05Lambda {
        private static void method(int num, Calcable lambda) {
            System.out.println(lambda.calc(num));
        }
        public static void main(String[] args) {
            method(‐10, n ‐> Math.abs(n));
        }
    }
    
    但是使用方法引用的更好写法是:
    public class Demo06MethodRef {
        private static void method(int num, Calcable lambda) {
            System.out.println(lambda.calc(num));
        }
        public static void main(String[] args) {
            method(‐10, Math::abs);
        }
    }

    通过super引用成员方法

    @FunctionalInterface
    public interface Greetable {
        void greet();
    }
    
    public class Human {
        public void sayHello() {
            System.out.println("Hello!");
        }
    }
    
    public class Man extends Human {
        @Override
        public void sayHello() {
            System.out.println("大家好,我是Man!");
        }
        
        //定义方法method,参数传递Greetable接口
        public void method(Greetable g){
            g.greet();
        }
        
        public void show(){
            //调用method方法,使用Lambda表达式
            method(()‐>{
                //创建Human对象,调用sayHello方法
                new Human().sayHello();
            });
    
            //简化Lambda
            method(()‐>new Human().sayHello());
            //使用super关键字代替父类对象
            method(()‐>super.sayHello());
        }
    }
    
    public class Man extends Human {
        @Override
        public void sayHello() {
            System.out.println("大家好,我是Man!");
        }
        //定义方法method,参数传递Greetable接口
        public void method(Greetable g){
            g.greet();
        }
        
        public void show(){
            method(super::sayHello);
        }
    }
    
    Lambda表达式: () -> super.sayHello()
    方法引用: super::sayHello

    通过this引用成员方法

    public class Husband {
        private void buyHouse() {
            System.out.println("买套房子");
        }
        private void marry(Richable lambda) {
            lambda.buy();
        }
        public void beHappy() {
            marry(() ‐> this.buyHouse());
        }
    }
    
    public class Husband {
        private void buyHouse() {
            System.out.println("买套房子");
        }
        private void marry(Richable lambda) {
            lambda.buy();
        }
        public void beHappy() {
            marry(this::buyHouse);
        }
    }

    类的构造器引用

    public class Person {
        private String name;
        public Person(String name) {
            this.name = name;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }
    
    public interface PersonBuilder {
        Person buildPerson(String name);
    }
    
    public class Demo09Lambda {
        public static void printName(String name, PersonBuilder builder) {
            System.out.println(builder.buildPerson(name).getName());
        }
        public static void main(String[] args) {
            printName("赵丽颖", name ‐> new Person(name));
        }
    }
    
    public class Demo10ConstructorRef {
        public static void printName(String name, PersonBuilder builder) {
            System.out.println(builder.buildPerson(name).getName());
        }
        public static void main(String[] args) {
            printName("赵丽颖", Person::new);
        }
    }
    
    Lambda表达式: name -> new Person(name)
    方法引用: Person::new

    数组的构造器引用

    @FunctionalInterface
    public interface ArrayBuilder {
        int[] buildArray(int length);
    }
    
    public class Demo11ArrayInitRef {
        private static int[] initArray(int length, ArrayBuilder builder) {
            return builder.buildArray(length);
        }
        public static void main(String[] args) {
            int[] array = initArray(10, length ‐> new int[length]);
        }
    }
    
    public class Demo12ArrayInitRef {
        private static int[] initArray(int length, ArrayBuilder builder) {
            return builder.buildArray(length);
        }
        public static void main(String[] args) {
            int[] array = initArray(10, int[]::new);
        }
    }
    
    Lambda表达式: length -> new int[length]
    方法引用: int[]::new
  • 相关阅读:
    C语言中链接影响程序的细节
    Object类的方法
    基于误差反向传播法的神经网络学习的全貌图
    Attention Scaling for Crowd Counting
    plt画图
    机器学习相关技巧
    数值微分
    Noip2017 Day2 T1 奶酪
    串(string)
    八皇后问题
  • 原文地址:https://www.cnblogs.com/alice-bj/p/12470852.html
Copyright © 2020-2023  润新知