Java8 的流式操作

攻城獅07發表於2019-10-11

1 流的建立

1.1 流的建立方法

    建立流的方法有三種,分別是:Stream.of()、Stream.iterate()、Stream.generate(),然後,分別看一下這三個方法的宣告。

static <T> Stream<T> of(T... values)
//Stream.of():引數很簡單,就是一系列的泛型引數。

static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)
//Stream.iterate():第一個引數是一個初始值,第二個引數是一個操作。

static <T> Stream<T> generate(Supplier<T> s)
//Stream.generate():引數就是一個Supplier的供給型的引數。
複製程式碼

1.2 流的建立方法舉例

@Test
public void testCreateStream() {
    //利用Stream.of方法建立流
    Stream<String> stream = Stream.of("hello", "world", "Java8");
    stream.forEach(System.out::println);
    System.out.println("##################");
    
    //利用Stream.iterate方法建立流
    Stream.iterate(10, n -> n + 1).limit(5).collect(Collectors.toList()).forEach(System.out::println);
    System.out.println("##################");
    
    //利用Stream.generate方法建立流
    Stream.generate(Math::random).limit(5).forEach(System.out::println);
    System.out.println("##################");
    
    //從現有的集合中建立流
    List<String> strings = Arrays.asList("hello", "world", "Java8");
    String string = strings.stream().collect(Collectors.joining(","));
    System.out.println(string);
}
複製程式碼

2 流的操作

2.1 裝箱流

    double流想要轉換為 List,有 3 種比較好的解決方法.
利用 boxed 方法

    利用 boxed 方法,可以將 DoubleStream 轉換為 Stream ,例如;

DoubleStream.of(1.0, 2.0, 3.0)
                .boxed()
                .collect(Collectors.toList());
複製程式碼

利用 mapToObj 方法

    利用 mapToObj 方法也可以實現上面的功能,另外,也提供了 mapToInt、mapToLong、mapToDouble 等方法將基本型別流轉換為相關包裝型別。

DoubleStream.of(1.0, 2.0, 3.0)
                .mapToObj(Double::valueOf)
                .collect(Collectors.toList());
複製程式碼

collect 方法

    一般情況下,我們利用 collect 方法的時候,都是用於將流的資料收集為基本型別的集合,例如;

stream.collect(Collectors.toList())
複製程式碼

    然而,collect 方法其實還有一種更加一般化的形式,如下;

<R> R collect(Supplier<R> supplier,
                        ObjIntConsumer<R> accumulator,
                        BiCnsumer<R,R> combiner)
 
第一個引數是一個供給器,相當於初始化一個容器,
第二個引數是累加器,相當於給初始化的容器賦值,
第三個引數是組合器,相當於將這些元素全部組合到一個容器。
複製程式碼
List<Double> list = DoubleStream.of(1.0, 2.0, 3.0)
                .collect(ArrayList<Double>::new, ArrayList::add, ArrayList::addAll);

第一個引數:使用一個靜態方法初始化一個 List 容器,
第二個引數:使用靜態方法 add ,新增元素,
第三個引數:使用靜態方法 addAll ,用於聯合所有的元素。
複製程式碼

2.2 字串與流之間的轉換

    String 轉為流有兩種方法,分別是 java.lang.CharSequence 介面定義的預設方法 chars 和 codePoints

@Test
public void testString2Stream() {
    String s = "hello world Java8".codePoints()//轉換成流
            .collect(StringBuffer::new,
                    StringBuffer::appendCodePoint,
                    StringBuffer::append)//將流轉換為字串
            .toString();

    String s1 = "hello world Java8".chars()//轉換成流
            .collect(StringBuffer::new,
                    StringBuffer::appendCodePoint,
                    StringBuffer::append)//將流轉換為字串
            .toString();
}
複製程式碼

2.3 流的對映 map 與 flatMap

    需要將一個集合的物件的某一個欄位取出來,然後再存到另外一個集合中

Java8 之前實現
@Test
public void testList() {
    List<Person> list = new ArrayList<>();
    List<Friend> friends = new ArrayList<>();
    friends.add(new Friend("Java5"));
    friends.add(new Friend("Java6"));
    friends.add(new Friend("Java7"));
    Person person = new Person();
    person.setFriends(friends);
    list.add(person);

    List<String> strings = new ArrayList<>();

    for(Person p : list){
        strings.add(p.getName());
    }
}
複製程式碼
Java8 使用 map 和 flatMap
<R> Stream<R> map(Function<? super T,? extends R> mapper)
<R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)
複製程式碼
@Test
public void testMapAndFlatMap() {
    List<Person> list = new ArrayList<>();
    List<Friend> friends = new ArrayList<>();
    friends.add(new Friend("Java5"));
    friends.add(new Friend("Java6"));
    friends.add(new Friend("Java7"));
    Person person = new Person();
    person.setFriends(friends);
    list.add(person);

    //對映出名字
    List<String> strings = list.stream().map(Person::getName).collect(Collectors.toList());
    //map 方法,引數給定 Person::getName 對映出 name,然後再用 collect 收集到 List 中
    
    //對映出朋友
    List<List<Friend>> collect = list.stream().map(Person::getFriends).collect(Collectors.toList());
    //返回值是 List<List<Friend>>,集合裡面還包著集合,處理有點麻煩
    
    List<Friend> collect1 = list.stream().flatMap(friend -> friend.getFriends().stream()).collect(Collectors.toList());
    //atMap 的方法能夠“展平”包裹的流,這就是 map 和 flatMap 的區別。
}
複製程式碼

2.4 流的連線

    流的連線有兩種方式,如果是兩個流的連線,使用 Stream.concat 方法,如果是三個及三個以上的流的連線,就使用 Stream.flatMap 方法。

@Test
public void testConcatStream() {
    //兩個流的連線
    Stream<String> first = Stream.of("sihai", "sihai2", "sihai3");
    Stream<String> second = Stream.of("sihai4", "sihai5", "sihai6");
    Stream<String> third = Stream.of("siha7", "sihai8", "sihai9");
    Stream<String> concat = Stream.concat(first, second);

    //多個流的連線
    Stream<String> stringStream = Stream.of(first, second, third).flatMap(Function.identity());
}
複製程式碼

3 流的規約操作

3.1 內建的規約操作

    基本型別流都有內建的規約操作。包括average、count、max、min、sum、summaryStatistics,前面的幾個方法相信不用說了,summaryStatistics 方法是前面的幾個方法的結合,下面我們看看他們如何使用。

@Test
public void testReduce1() {
    String[] strings = {"hello", "sihai", "hello", "Java8"};
    long count = Arrays.stream(strings)
            .map(String::length)
            .count();
    System.out.println(count);//4
    System.out.println("##################");

    int sum = Arrays.stream(strings)
            .mapToInt(String::length)
            .sum();
    System.out.println(sum);//20
    System.out.println("##################");

    OptionalDouble average = Arrays.stream(strings)
            .mapToInt(String::length)
            .average();
    System.out.println(average);//OptionalDouble[5.0]
    System.out.println("##################");

    OptionalInt max = Arrays.stream(strings)
            .mapToInt(String::length)
            .max();
    System.out.println(max);//OptionalInt[5]
    System.out.println("##################");

    OptionalInt min = Arrays.stream(strings)
            .mapToInt(String::length)
            .min();
    System.out.println(min);//OptionalInt[5]

    DoubleSummaryStatistics statistics = DoubleStream.generate(Math::random)
            .limit(1000)
            .summaryStatistics();
    System.out.println(statistics);//DoubleSummaryStatistics{count=1000, sum=490.322996, min=0.000175, average=0.490323, max=0.999745}
}
複製程式碼

3.2 基本的規約操作

    基本的規約操作是利用前面講過的 reduce 方法實現的,IntStream 介面定義了三種 reduce 方法的過載形式,如下;

OptionalInt reduce(IntBinaryOperator op)

int reduce(int identity, IntBianryOperator op)

<U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BianryOperator<U> combiner)
identity 引數就是初始化值的意思,
IntBianryOperator 型別的引數就是操作,例如 lambda 表示式;
BianryOperator<U> combiner是一個組合器,在前面有講過。
複製程式碼
@Test
public void testReduce2() {
    int sum = IntStream.range(1, 20)
            .reduce((x, y) -> x + y)
            .orElse(0);
    System.out.println(sum);//190
    System.out.println("##################");

    int sum2 = IntStream.range(1, 20)
            .reduce(0, (x, y) -> x + 2 * y);
    System.out.println(sum2);//380
    System.out.println("##################");

    int sum3 = IntStream.range(1, 20)
            .reduce(0, Integer::sum);
    System.out.println(sum3);//190

}
第一個是120累加的操作,第二個以0為初始值,然後2倍累加,第三個是以0為初始值,累加。
複製程式碼

3.3 流的計數

    流的數量統計有兩種方法,分別是 Stream.count() 方法和 Collectors.counting() 方法。

@Test
public void testStatistics() {
    //統計數量
    String[] strings = {"hello", "sihai", "hello", "Java8"};
    long count = Arrays.stream(strings)
            .count();
    System.out.println(count);
    System.out.println("##################");

    Long count2 = Arrays.stream(strings)
            .collect(Collectors.counting());
    System.out.println(count2);

}
複製程式碼

4 流的查詢與匹配

4.1 流的查詢

    流的查詢 Stream 介面提供了兩個方法 findFirst 和 findAny。
    findFirst 方法返回流中的第一個元素的 Optional,而 findAny 方法返回流中的某個元素的 Optional。

String[] strings = {"hello", "sihai", "hello", "Java8"};
    Optional<String> first = Arrays.stream(strings).findFirst();
    System.out.println(first.get());
    System.out.println("##################");

    Optional<String> any = Arrays.stream(strings).findAny();
    System.out.println(any.get());
    System.out.println("##################");
複製程式碼

4.2 流的匹配

    流的匹配 Stream 介面提供了三個方法,分別是 anyMatch(任何一個元素匹配,返回 true)、allMatch(所有元素匹配,返回 true)、noneMatch(沒有一個元素匹配,返回 true)。

boolean b = Stream.of(1, 2, 3, 4, 5, 10)
                .anyMatch(x -> x > 5);
        System.out.println(b);
        System.out.println("##################");

        boolean b2 = Stream.of(1, 2, 3, 4, 5, 10)
                .allMatch(x -> x > 5);
        System.out.println(b2);
        System.out.println("##################");

        boolean b3 = Stream.of(1, 2, 3, 4, 5, 10)
                .noneMatch(x -> x > 5);
        System.out.println(b3);
複製程式碼

5 流的總結

流的建立方法。
流的系列操作,包括裝箱流、字串與流之間的轉換、流和對映 map 和 flatMap、流的連線。
流的規約操作
流的查詢與匹配
複製程式碼
作者:歐陽思海
連結:http://blog.ouyangsihai.cn/java8-de-stream-liu-shi-cao-zuo-zhi-wang-zhe-gui-lai.html
來源:ouyangsihai.cn
複製程式碼

相關文章