Java Stream & Method Reference

Java Stream & Method Reference

1. Stream flow

1.1 Overview

Conventional multi-step through code set

/*
    传统的方式遍历集合,过滤集合,打印输出满足条件的集合
 */
import java.util.ArrayList;
public class Demo01LIst {
    public static void main(String[] args) {
        // 创建list集合,存储姓名
        ArrayList<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("赵敏");
        list.add("周芷若");
        list.add("张强");
        list.add("张三丰");

        ArrayList<String> listA = new ArrayList<>();
        for (String s : list) {
            // 对list集合过滤,将张字开头的添加到listA集合中
            if (s.startsWith("张")) {
                listA.add(s);
            }
        }

        ArrayList<String> listB = new ArrayList<>();
        for (String s : listA) {
            // 对listA集合过滤,只要名字长度为3的
            if (s.length() == 3) {
                listB.add(s);
            }
        }

        // 遍历listB集合,打印输出
        for (String s : listB) {
            System.out.println(s);
        }
    }
}

Stream more preferably wording

import java.util.ArrayList;
/*
    使用Stream流的方式,遍历集合,对集合中的数据进行过滤
    Stream流式JDK1.8之后出现的
    关注的是做什么,而不是怎么做
 */
public class Demo02Stream {
    public static void main(String[] args) {
        // 创建list集合,存储姓名
        ArrayList<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("赵敏");
        list.add("周芷若");
        list.add("张强");
        list.add("张三丰");

        // 对list集合过滤,将张字开头的添加到listA集合中
        // 对listA集合过滤,只要名字长度为3的
        // 遍历listB集合,打印输出

        list.stream()
                .filter(name -> name.startsWith("张"))
                .filter(name -> name.length() == 3)
                .forEach(name -> System.out.println(name));

    }
}

Overview 1.2 Streaming thought

Note: Please Forget the traditional IO streams inherent impression!

filter, map, skipIs the operation of the function model, and the set of elements is not actually processed. Only when the end of the method countwhen executed, the entire operation will be performed in accordance with the model specified policy. And thanks to the delay in the implementation of the Lambda characteristic.

Note: "Stream Flow" is actually a collection of elements of the model function, it is not set, the data structure is not, in itself does not store any element (or address value)

Stream (flow) is an element from the data source queue

  • Element is a particular type of object, a queue is formed. In Java Stream and does not store elements, but on-demand computing.
  • Data source source stream. It may be a collection, such as an array.

Collection different previous operation, Stream operation there are two basic characteristics:

  • PIPELINING : intermediate operations return stream object itself. Such operations may be connected in series to a plurality of pipes, as flow style (fl uent style). This will optimize the operation, such as delayed execution (laziness) and short circuit (short-circuiting).
  • Internal iterations : In the past on the way to traverse the collection is enhanced by or for the Iterator, explicit in the aggregate external iterate, this is called an external iteration. Stream provides a way inside iterative flow traversal method can be called directly.

When a stream of time, typically includes three basic steps of: obtaining a data source (Source) → → perform the data conversion operation to obtain the desired result, converting each Stream object does not change the original, a new Stream object returns ( there may be multiple conversions), which may be arranged to allow the operation same as the chain thereof, into a conduit. 

1.3 The acquisition stream

  • java.util.stream.Stream <T>is the most common stream interface Java 8 new entrants. (This is not a function interface.)

Gets a stream of very simple, there are several common ways:

  • All Collectioncollections are available through streamthe acquisition stream default method.
    • default Stream<E> stream()
  • Stream interface static method of, an array of the corresponding stream can be acquired.
    • static <T> Stream<T> of(T... values)
    • Parameter is a variable parameter, then we can pass an array.
  • Example:
/*
获取stream流的两种方式
1. 所有Collection集合都可以通过默认方法 stream获取流
2. 可以使用Stream接口中的静态方法,of,将数组转换成流对象
 */
import java.util.*;
import java.util.stream.Stream;

public class Demo03GetStream {
    public static void main(String[] args) {
        // 把集合转换为Stream流
        List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();

        Set<String> set = new HashSet<>();
        Stream<String> stream2 = set.stream();

        Map<String, String> map = new HashMap<>();
        // 获取键,存储到set集合中
        Set<String> keySet = map.keySet();
        Stream<String> stream3 = keySet.stream();
        // 获取值,存储到Collection集合
        Collection<String> values = map.values();
        Stream<String> stream4 = values.stream();

        // 获取键值对(键与值的映射关系 entrySet)
        Set<Map.Entry<String, String>> entries = map.entrySet();
        Stream<Map.Entry<String, String>> stream5 = entries.stream();

        // 把数组转换为Stream流
        Stream<Integer> stream6 = Stream.of(1, 2, 3, 4, 5);

        // 可变参数可以传递数组
        Integer[] arr = {1, 3, 4, 5};
        Stream<Integer> stream7 = Stream.of(arr);

        String[] arr2 = {"a", "b", "c", "d"};
        Stream<String> stream8 = Stream.of(arr2);



    }
}

1.4 common method

Very rich operation flow model, which can be divided into two methods:

  • Delayed method : return type is still Streamits own type of method interface, support for chained calls. (Except termination method, the other methods are delaying.)
  • End of method : the return value type is no longer Streamits own type of method interfaces, and therefore will not support a similar StringBuilderkind of chained calls. In this section, the method comprising termination countand forEachmethods.

① processed one by one: forEach

Although the name forEach, but the for loop for-each different.

  • void forEach(Consumer<? super T> action);
  • The method receives a Consumer interface function, each stream will function element to the process.
  • Consumer interface is a function of the consumer interface, you can pass Lambda expressions, consumption data.

Jot:
forEach method for traversing data stream, is a method end, can not continue to call other methods Stream stream after traversal.

  • Example:
import java.util.stream.Stream;

public class Demo04ForEach {
    public static void main(String[] args) {
        // 获取一个Stream流
        Stream<String> stream = Stream.of("迪丽热巴", "古力娜扎", "马儿扎哈");
        // 使用forEach,参数传递Lambda表达式
        /*stream.forEach((String name) -> {
            System.out.println(name);
        });*/

        // 优化Lambda表达式
        stream.forEach(name -> System.out.println(name));
    }
}

/*
print result:
迪丽热巴
古力娜扎
马儿扎哈
*/

② filter: filter

  • filter: for filtering the data stream Stream.
  • Stream filter(Predicate<? super T> predicate) ;
  • Predicate filter method parameter is a function interface, Lambda expressions can be transferred, the data filtering.
  • Predicate abstract methods:
    • boolean test(T t);
  • Example:
import java.util.stream.Stream;
// Stream<T> filter(Predicate<? super T> predicate);
public class Demo05Filter {
    public static void main(String[] args) {
        // 获取Stream流
        Stream<String> stream = Stream.of("张三丰", "张三疯", "周芷若", "赵敏", "张翠山");
        // 使用 filter方法进行过滤
        // stream.filter((String name) -> {name.startsWith("张");});
        // 使用 filter之后,返回一个新的流
        Stream<String> newStream = stream.filter(name -> name.startsWith("张"));
        // 逐一输出
        newStream.forEach(name -> System.out.println(name));
        /*
            Stream流属于管道流,只能被消费(使用)一次
            第一个Stream流调用完毕,数据就会流到下一个Stream上
            而这时第一个Stream流已经使用完毕,就会关闭了
            所以第一个Stream流就不能再调用方法了
 IllegalStateException: stream has already been operated upon or closed
         */
        // 已经被使用一次了,再次使用会抛出异常
        // stream.forEach(name -> System.out.println(name));
    }
}

③ mapping: map

  • If you need to flow to another flow element maps, map method can be used.
  • <R> Stream<R> map(Function<? super T,? extends R> mapper) ;
  • Function The interface requires a functional interface parameters, type T can convert the current data stream to another stream R type.
  • Function of abstract methods:
    • R apply(T t);
  • Example:
import java.util.stream.Stream;

public class Demo06Map {
    public static void main(String[] args) {
        // 获取一个String类的Stream流
        Stream<String> stream = Stream.of("1", "2", "3", "4");
        // 将String类型的整数,转换(映射)为 Integer类型的整数
        Stream<Integer> stream2 = stream.map((String s) -> {
            return Integer.parseInt(s);
        });
        // 遍历 stream2流
        stream2.forEach(num -> System.out.println(num));
    }
}

④ count the number of: count

As the old set Collectionamong the sizemethods as Streamproviding counta method wherein the number of elements to obtain.

  • count: Stream statistics for the number of elements in the flow.
  • long count();
  • count method is a method end, the return value is a long integer type, so you can not call other methods of the Stream flow.

  • Example:

import java.util.ArrayList;
import java.util.stream.Stream;

public class Demo07Count {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);

        Stream<Integer> stream = list.stream();
        long count = stream.count();
        System.out.println(count);
    }
}

⑤ access to the first few: limit

  • limit: elements for intercepting stream.
  • The method can be taken convection limit, access only the first n.
  • Stream<T> limit(long maxSize);
    • Parameter is a long type, if the length is greater than the current set of parameters, it is truncated; otherwise, no operation is performed.
  • limit is a method of delaying only the elements in the convection taken, it returns a new stream, it can continue to invoke other methods Stream stream.

  • Example:

import java.util.Arrays;
import java.util.stream.Stream;

public class Demo08Limit {
    public static void main(String[] args) {
        String[] arr = {"喜羊羊", "美羊羊", "暖羊羊", "灰太狼", "红太狼"};
        Stream<String> stream = Arrays.stream(arr);
        Stream<String> stream2 = stream.limit(3); // 截取前3个元素,并返回新的Stream流
        stream2.forEach(name -> System.out.println(name));
    }
}

⑥ skip the first few: skip

  • skip: element for skipping;
  • If you want to skip the first few elements, a method may be used to obtain a new skip stream after a taken.
  • Stream<T> skip(long n);
    • If the length of the flow is greater than n, the n skipped before; otherwise it will give a length of 0 is empty stream.
import java.util.stream.Stream;

public class Demo09Skip {
    public static void main(String[] args) {
        String[] arr = {"喜羊羊", "美羊羊", "暖羊羊", "灰太狼", "红太狼"};
        Stream<String> stream = Stream.of(arr);
        // 跳过前3个
        Stream<String> stream2 = stream.skip(3);
        stream2.forEach(name -> System.out.println(name));
    }
}

⑦ combination: concat

  • concat: combined together for the flow.
  • If there are two streams, desirable combined into one stream, you can use Streamstatic method interfaceconcat
  • public static Stream concat(Stream<? extends T> a, Stream<? extends T> b)
  • Example:
import java.util.stream.Stream;

/*
public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
 */
public class Demo10Concat {
    public static void main(String[] args) {
        String[] arr = {"喜羊羊", "美羊羊", "暖羊羊", "灰太狼", "红太狼"};
        Stream<String> stream1 = Stream.of(arr);
        Stream<String> stream2 = Stream.of("张三丰", "张三疯", "周芷若", "赵敏", "张翠山");
        // 把以上两个流组合成为一个流
        Stream<String> concat = Stream.concat(stream1, stream2);
        // 遍历输出
        concat.forEach(name -> System.out.println(name));

    }

}

Exercise 1.5: processing a set of elements (conventional)

    import java.util.ArrayList;

/*
题目: 
现在有两个 ArrayList 集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以下若干操作步骤: 
    练习:集合元素处理(传统方式)
        现在有两个ArrayList集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以下若干操作步骤:
        1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
        2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。
        3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
        4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。
        5. 将两个队伍合并为一个队伍;存储到一个新集合中。
        6. 根据姓名创建Person对象;存储到一个新集合中。
        7. 打印整个队伍的Person对象信息。
 */
public class DemoStreamTest {
    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("洪七公");
        // 1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
        ArrayList<String> oneA = new ArrayList<>();
        for (String name : one) {
            if (name.length() == 3) {
                oneA.add(name);
            }
        }
        // 2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。
        ArrayList<String> oneB = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            oneB.add(oneA.get(i));
        }
        //第二支队伍
        ArrayList<String> two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("张无忌");
        two.add("赵丽颖");
        two.add("张三丰");
        two.add("尼古拉斯赵四");
        two.add("张天爱");
        two.add("张二狗");
        // 3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
        ArrayList<String> twoA = new ArrayList<>();
        for (String name : two) {
            if (name.startsWith("张")) {
                twoA.add(name);
            }
        }
        // 4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。
        ArrayList<String> twoB = new ArrayList<>();
        for (int i = 2; i < twoA.size(); i++) {
            twoB.add(twoA.get(i));
        }
        // 5. 将两个队伍合并为一个队伍;存储到一个新集合中。
        ArrayList<String> all = new ArrayList<>();
        all.addAll(twoB);
        all.addAll(oneB);
        // 6. 根据姓名创建Person对象;存储到一个新集合中。
        ArrayList<Person> list = new ArrayList<>();
        for (String s : all) {
            list.add(new Person(s));
        }
        for (Person person : list) {
            System.out.println(person);
        }
    }
}

1.6 Exercise: a collection of processing elements (Stream stream mode)

import java.util.ArrayList;
import java.util.stream.Stream;

/*
题目: 
现在有两个 ArrayList 集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以下若干操作步骤: 
    练习:集合元素处理(传统方式)
        现在有两个ArrayList集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以下若干操作步骤:
        1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
        2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。
        3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
        4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。
        5. 将两个队伍合并为一个队伍;存储到一个新集合中。
        6. 根据姓名创建Person对象;存储到一个新集合中。
        7. 打印整个队伍的Person对象信息。
 */
public class DemoStreamTest2 {
    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("洪七公");
        // 1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
        // 2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。
        Stream<String> oneStream = one.stream().filter(name -> name.length() == 3).limit(3);
        //第二支队伍
        ArrayList<String> two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("张无忌");
        two.add("赵丽颖");
        two.add("张三丰");
        two.add("尼古拉斯赵四");
        two.add("张天爱");
        two.add("张二狗");
        // 3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
        // 4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。
        Stream<String> twoStream = two.stream().filter(name -> name.startsWith("张")).skip(2);
        // 5. 将两个队伍合并为一个队伍;存储到一个新集合中。
        // 6. 根据姓名创建Person对象;存储到一个新集合中。
        // 7. 打印整个队伍的Person对象信息。
        Stream.concat(oneStream, twoStream).map(name -> new Person(name)).forEach(name-> System.out.println(name));
    }
}

2. The reference method

2.1 General Introduction

The double colon ::is the reference operator, and it is referred to in the expression method reference . If the function program to be expressed already present in Lambda implemented a method, the method can be cited as the Lambda replacement by the double colon.

  • Printable interface
/*
    定义一个打印的函数式接口
 */
@FunctionalInterface
public interface Printable {
    // 对字符串的抽象方法
    public abstract void print(String s);
}
  • Demo01Printable.java
public class Demo01Printable {
    // 定义一个方法,参数传递Printable接口,对字符串进行打印
    public static void printString(Printable p) {
        p.print("HelloWorld");
    }

    public static void main(String[] args) {
        printString((s) -> {
            System.out.println(s);
        });
    /*
        分析:
            Lambda表达式的目的:打印参数传递的字符串
            把参数 s 传递给 System.out对象,调用 out对象中的方法println对字符串输出
            使用前提(注意):
                1.System.out对象已经存在。
                2.println方法也是存在的。
            所以我们可以直接使用方法引用来优化 Lambda表达式
            可以使用System.out方法直接引用(调用)println方法
     */
        printString(System.out::println);
    }
}

Semantic Analysis

In the example embodiment, System.outa subject already overloaded print(String x)method just what we need then for printStringa method of functional interface parameters, compare the following two way completely equivalent:

  1. Lambda written: s -> System.out.println (s);
  2. Methods cited wording: System.out :: println ();
  • The first one kind of semantic means: get after the parameters by Lambda hand, then passed to the System.out.println()method to handle.
  • A second equivalent semantic means: directly from System.outthe printlnmethods substituted Lambda. Both versions perform exactly the same effect, while the second method cited reusing existing programs more concise.

Note:
the method cited in the parameters passed in a certain approach Lambda can receive type, otherwise it will throw an exception.

2.2 reference method [members] by object name

  • Printable interface
/*
    定义一个打印的函数式接口
 */
@FunctionalInterface
public interface Printable {
    // 对字符串的抽象方法
    public abstract void print(String s);
}
  • MethodRefObject.java
public class MethodRefObject {
    // 定义一个成员方法,参数传递字符串,将字符串大写输出
    public void printUpperCaseString(String s) {
        System.out.println(s.toUpperCase());
    }
}
  • DemoObjectMethodReference.java
public class DemoObjectMethodReference {
    // 定义一个方法,参数传递Printable接口
    public static void printString(Printable p) {
        p.print("Hello Java");
    }

    public static void main(String[] args) {
        printString((s) -> {
            MethodRefObject obj = new MethodRefObject();
            obj.printUpperCaseString(s);
        });
        /*
           使用方法引用对Lambda进行优化
           使用前提:对象和成员方法已经存在
         */
        // 创建对象
        MethodRefObject obj = new MethodRefObject();
        printString(obj::printUpperCaseString);
    }
}

2.3 referenced by the class name] [static methods

  • Calcable Interface
public interface Calcable {
    // 定义一个抽象方法,参数传递一个整数,进行取模运算
    public int methodAbs(int num);
}
  • DemoStaticMethodRef.java
public class DemoStaticMethodRef {
    // 定义一个方法,参数传递一个int型整数,一个Calcalbe接口
    public static int method(int num, Calcable c) {
        return c.methodAbs(num);
    }

    public static void main(String[] args) {
        // 调用method方法,传递一个整数,取模运算
        int num = method(-10, (n) -> {
            return Math.abs(n);
        });
        System.out.println(num);
        /*
        通过类名称引用静态成员方法的前提:
            1. 类存在
            2. 静态成员方法存在且已经实现了
         */
        int num2 = method(-100, Math::abs);
        System.out.println(num2);
    }
}

2.4 Common reference member of the parent class of the super

  • Greetable Interface
// 定义一个问候的接口
public interface Greetable {
   public abstract void greet();
}
  • Fu.java
public class Fu {
    // 定义一个sayHello方法
    public void sayHello() {
        System.out.println("我是师父");
    }

}
  • Zi.java
/*
通过super引用父类的普通成员方法
 */
public class Zi extends Fu {
    // 覆盖重写父类的 sayHello方法
    @Override
    public void sayHello() {
        System.out.println("我是徒弟");
    }

    // 定义一个方法,参数传递Greetable接口
    public void method(Greetable g) {
        g.greet();

    }

    // 展示输出的方法,参数传递一个Greetable接口
    public void show() {
        /*method(() -> {
            Fu fu = new Fu();
            fu.sayHello();
        });*/
        // method(() -> super.sayHello());
        method(super::sayHello);
    }
}
  • main method
public class DemoMain {
    public static void main(String[] args) {
        new Zi().show();
    }
}

2.5 this reference by ordinary members of the present class method

  • Richable Interface
public interface Richable {
    // 定义一个买东西的方法
    public abstract void buy();
}
  • Husband.java
public class Husband {
    // 定义买房子的方法
    public void buyHouse() {
        System.out.println("杭州一套小楼!");
    }

    // 定义结婚的方法, 参数传递Richable接口
    public void marry(Richable r) {
        r.buy();
    }

    // 高兴方法,因为要结婚
    public void soHappy() {
        //  this.marry(() -> {this.buyHouse();});
        /*
            this和调用的本类成员方法已经存在且实现
            可以使用this引用本类的成员方法
         */
        marry(this::buyHouse);
    }

    public static void main(String[] args) {
        new Husband().soHappy();
    }
}

2.6 class constructor (the constructor) cited

  • PersonBuilder Interface
@FunctionalInterface
public interface PersonBuilder {
    // 定义方法,用来根据传递的姓名,创建Person对象,并返回
    public abstract Person builderPerson(String name);
}
  • DemoPerson.java
public class DemoPerson {
    // 定义一个方法,参数传递姓名和PersonBuilder接口,打印对象姓名
    public static void printName(String name, PersonBuilder pb) {
        Person person = pb.builderPerson(name);
        System.out.println(person);
    }

    public static void main(String[] args) {
        // printName("迪丽热巴", (s) -> new Person(s));
        /*
            使用方法引用优化Lambda表达式
            构造方法:new Person(String name);已知
            创建对象:new 已知
            就可以使用Person引用new创建对象
         */
        printName("周元", Person::new); // 使用Person类的带参构造方法,创建对象
    }
}

2.7 constructor reference to an array

  • ArrayBuilder Interface
/*
    数组创建接口
 */
@FunctionalInterface
public interface ArrayBuilder {
    // 根据参数传递的长度,创建int型指定长度的数组
    public abstract int[] builderArray(int length);
}
  • DemoArray.java
import java.util.Arrays;

public class DemoArray {
    /*
        定义一个方法,参数传递要创建数组的长度和ArrayBuilder接口
        方法内部传递的长度使用ArrayBuilder中的方法创建数组并返回
     */
    public static int[] creatArray(int length, ArrayBuilder ab) {
        return ab.builderArray(length);
    }

    public static void main(String[] args) {
        int[] arrA = creatArray(10, (len) -> new int[len]);
        System.out.println(Arrays.toString(arrA));
        System.out.println(arrA.length);
        /*
            创建的是 int[] 类型的数组
            数组的长度已知
            使用 int[]引用new,根据传递的长度创建数组
         */
        int[] arrB = creatArray(10, int[]::new);
        System.out.println(arrB.length);
    }
}

Guess you like

Origin www.cnblogs.com/blog-S/p/11520428.html