1. 程式人生 > >java8-流的操作

java8-流的操作

有一個 處理 類型 product har cti 最小 mit pan

流的操作

流的使用一般包括三件事:

一個數據源來執行一個查詢;

一個中間操作鏈,形成一條流的流水線;

一個終端操作,執行流水線,並能生成結果

中間操作

操作 類型 返回類型 操作參數 函數描述符
filter 中間 Stream Predicate T -> boolean
map 中間 Stream Function T->R
limit 中間 Stream
sorted 中間 Stream Comparator (T,T)->int
distinct 中間 Stream

終端操作

操作 類型 目的
forEach 終端 消費流中的每個元素並對其應用Lambda.這一操作返回void
count 終端 返回流中元素的個數,這一操作返回long
collect 終端 把流歸約成一個集合,比如List,Map甚至是Integer

使用流

篩選

出了filter ,流還支持一個叫做distinct的方法,它會返回一個元素各異(根據流所生成元素的hashCode和equals方法實現)的流.例如,如下代碼會篩選出列表中所有的偶數,並確保沒有重復

List<Integer> numbers = Arrays.asList(1,2,1,3,3,2,4);
numbers.stream()
  .filter
(i -> i%2 ==0) .distinct() .forEach(System.out::println);

截斷流

流支持limit(n) 方法,該方法會返回一個不超過給定長度的流.

List<Dish> dishes = menu.stream()
.filter(d->d.getCalories()>300)
.limit(3)
.collect(toList());

跳過元素

流還支持skip(n)方法,返回一個扔掉了前n個元素的流.

List<Dish> dishes = menu.stream().filter(d->d.getCalories
()>300).skip(2).collect(toList());

映射

對流中每一個元素應用函數

例如: 如果要找出每道菜的名稱有多長,則如下:

List<Integer> dishNameLengths = menu.stream().map(Dish::getname).map(String::length).collect(toList())

流的扁平化

例如:給定單詞列表["hello","world"],你想要返回列表["h","e","l","l","o","w","o","r","l","d"]

利用上面的可能是:

words.stream().map(word->word.split("")).distinct().collect(toList());

這個方法的問題自傲與,傳遞給map方法的Lambda為每個單詞返回一個String[] (String列表),因此,map返回的流實際上是Stream< String[] >類型的.你真正想要的是用Stream< String > 來表示一個字符流

1.嘗試使用map和Arrays.stream()

String[] arrayOfWords = {"GoodBye","World"};
Stream< String> streamOfWords = Arrays.stream(arrayOfWords);    

2.使用flatMap

        String[] arrayOfWords = {"GoodBye","World"};
        Stream< String> streamOfWords = Arrays.stream(arrayOfWords);
        List<String[]> collect = streamOfWords.map(m -> m.split("")).collect(toList());
        System.out.println(collect);

例2:

給定列表[1,2,3] 和[3,4] ,返回[(1,3),(1,4),(2,3).....]

        List<Integer> integers = Arrays.asList(1, 2, 3);
        List<Integer> integers1 = Arrays.asList(3, 4);
        List<int[]> collect = integers.stream()
                .flatMap(i -> integers1.stream()
                        .map(j -> new int[]{i, j}))
                .collect(toList());
        collect.forEach(i -> {
            String s = Arrays.toString(i);
            System.out.println(s);
        });

例3:

擴展前面的例子,只返回綜合能被3正處的數對呢?

        List<Integer> int1 = Arrays.asList(1, 2, 3);
        List<Integer> int2 = Arrays.asList(3, 4);
        List<int[]> collect = int1.stream().flatMap(i -> int2.stream().filter(j -> (i + j) % 3 == 0).map(j -> new int[]{i, j})).collect(toList());
        System.out.println(collect);

查找和匹配

匹配
1.檢查謂詞是否至少匹配一個一元素

anyMatch方法可以回答"流中是否有一個元素能匹配給定的謂詞"

if(menu.stream().anyMatch(Dish::isVegetarian)){
    System.out.println("The menu is vegetarian friendly!!");    
}
2.檢查謂詞是否匹配所有元素

例如:用allMatch來看看菜品是否有利健康

boolean isHealthy = menu.stream().allMatch(d->d.getCalories()<1000);
3.確保流中沒有任何元素與給定的謂詞匹配 noneMatch
boolean isHealthy = menu.stream().noneMatch(d->d.getCalories()<1000);
查找
findAny方法將返回當前流中的任意元素
Optional<Dish> dish = menu.stream().filter(Dish::isVegetarian).findAny();
查找第一個元素
List<Integer> someNumbers = Arrays.asList(1,2,3,4,5);
Optional<Integer> firstSquareDivisibleByThree = 
    someNumbers.stream()
    .map(x->x*x)
    .filter(x->x%3 == 0)
    .findFirst();

歸約

元素求和 reduce

reduce接受兩個參數

一個初始值,這裏是0

一個BinaryOperator< T >來將兩個元素結合起來產生一個新值 ,這裏我們用的是lambda(a,b)->a+b;

int sum = numbers.stream().reduce(0,(a,b)->a+b);

相乘:

int product = numbers.stream().reduce(1,(a,b)->a*b);

在java 8 中,Integer類現有了一個靜態的sum方法來對兩個數求和

int num=numbers.stream().reduce(0,Integer::sum);

最大值:

Optional<Integer> max = numbers.stream().reduce(Intger::max)

最小值:

Optional<Integer> min = numbers.stream().reduce(Integer::min);

數值流

原始類型流特化

java 8 引入了三個原始類型特化流接口來專門支持處理數值流的方法:IntStream . DoubleStream 和LongStream,.

分別將流中的元素特化成為int,long ,double,從而避免了暗含裝箱成本

1.映射到數值流

將流轉化為特化版本的常用方法是mapToInt,mapToDouble ,mapToLong.

int calaories = menu.stream()
    .mapToInt(Dish::getCalories)//返回一個IntStream
    .sum();

2.轉換回対向流 boxed

IntStream intStream = menu.stream().mapToInt(Dish::getCalories);
Stream<Integer> stream = intStream.boxed();

3.默認值OptionalInt

IntStream中有個默認值0,所以,為了解決求最大值或最小值的問題 ,使用Optional原始類型特化版本:OptionalInt,

OptionalDouble,OptionalLong.

例如:要找到IntStream中的最大元素,可以調用max方法,它會返回一個OptionalInt:

OptionalInt maxCalories = menu.stream()
    .mapToInt(Dish::getCalories)
    .max();
//如果沒有最大值的話,可默認一個最大值
int max = maxCalories.orElse(1);

4.數值範圍

假如你想要生成1到100之間的所有數字. java8引入了兩個可以用於IntStream和LongStream的靜態方法,幫助生成這種範圍:range和rangeClosed. 這兩個方法都是第一個參數接受起始值,第二個參數接受結束值.但是range是不包含結束值的,而rangeClosed則包含結束值

IntStream evenNumbers = IntStream.rangeClosed(1,100)
    .filter(n -> n % 2 == 0);//一個從1到100的偶數流

System.out.println(evenNumbers.count());//從1到100有50個偶數

構建流

由值創建流

可以使用靜態方法Stream.of,通過顯示值創建一個流.它可以接受任意數量的參數

Strean<String> stream = Stream.of("java 8","Lambdas ","In ","Action");
stream.map(String::toUpperCase).forEach(System.out::println);
//使用empty得到一個空流
Stream<String> emptySteam = Stream.empty();
由數組創建流
int[] numbers = {2,3,4,11,13};
int sum = Arrays.stream(numbers).sum();
由文件生成流
long uniqueWords = 0;
try(Stream<String> lines = 
    Files.lines(Paths.get("data.txt"),Charset.defaultCharset())){
    uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
        .distinct()
        .count();
}catch(IOException e){
  
}
由函數生成流:創建無限流

Stream.iterate和Stream.generate ,一般來說應該使用limit(n)來對這種流加以限制,以避免打印無窮多個值

1.叠代

Stream.iterate(0,n -> n+2)
    .limit(10)
    .forEach(System.out::println);

2.生成

與iterate方法類似,generate方法也可生成一個無限流.但generate不是依次對每個新生成的值應用函數的.它接受一個Supplier< T >類型的Lambda提供新的值

Stream.generate(Math::random)
  .limit(5)
  .forEach(System.out::println);
//下面的代碼就是出安靜了一個斐波那契
IntSupplier fib = new IntSuppleir(){
    private int previous = 0;
    private int current = 1;
    public int getAsint(){
        int oldPrevious = this.previous;
        int nextValue = this.previous + this.current;
        this.previous = this.current;
        this.current = nextValue;
        return oldPrevious;
    }
};
IntStream.generate(fib).limit(10).forEach(System.out::println);

java8-流的操作