字節(jié)二面被問“Java Stream 流操作‘’务热?看完這篇忆嗜,教你自信應(yīng)對!

Stream將要處理的元素集合看作一種流崎岂,在流的過程中捆毫,借助Stream API對流中的元素進(jìn)行操作,比如:篩選冲甘、排序冻璃、聚合等响谓。

image-20210701194245361

Stream 的操作符大體上分為兩種:中間操作符終止操作符

中間操作符

對于數(shù)據(jù)流來說,中間操作符在執(zhí)行制定處理程序后省艳,數(shù)據(jù)流依然可以傳遞給下一級的操作符娘纷。

中間操作符包含8種(排除了parallel,sequential,這兩個(gè)操作并不涉及到對數(shù)據(jù)流的加工操作):

  1. map(mapToInt,mapToLong,mapToDouble) 轉(zhuǎn)換操作符跋炕,把比如A->B赖晶,這里默認(rèn)提供了轉(zhuǎn)int,long辐烂,double的操作符遏插。
  2. flatmap(flatmapToInt,flatmapToLong,flatmapToDouble) 拍平操作比如把 int[]{2,3,4} 拍平 變成 2,3纠修,4 也就是從原來的一個(gè)數(shù)據(jù)變成了3個(gè)數(shù)據(jù)胳嘲,這里默認(rèn)提供了拍平成int,long,double的操作符。
  3. limit 限流操作扣草,比如數(shù)據(jù)流中有10個(gè) 我只要出前3個(gè)就可以使用了牛。
  4. distint 去重操作,對重復(fù)元素去重辰妙,底層使用了equals方法鹰祸。
  5. filter 過濾操作,把不想要的數(shù)據(jù)過濾密浑。
  6. peek 挑出操作蛙婴,如果想對數(shù)據(jù)進(jìn)行某些操作,如:讀取尔破、編輯修改等街图。
  7. skip 跳過操作,跳過某些元素懒构。
  8. sorted(unordered) 排序操作台夺,對元素排序,前提是實(shí)現(xiàn)Comparable接口痴脾,當(dāng)然也可以自定義比較器颤介。

終止操作符

數(shù)據(jù)經(jīng)過中間加工操作,就輪到終止操作符上場了赞赖;

終止操作符就是用來對數(shù)據(jù)進(jìn)行收集或者消費(fèi)的滚朵,數(shù)據(jù)到了終止操作這里就不會(huì)向下流動(dòng)了,終止操作符只能使用一次前域。

  1. collect 收集操作辕近,將所有數(shù)據(jù)收集起來,這個(gè)操作非常重要匿垄,官方的提供的Collectors 提供了非常多收集器移宅,可以說Stream 的核心在于Collectors归粉。
  2. count 統(tǒng)計(jì)操作,統(tǒng)計(jì)最終的數(shù)據(jù)個(gè)數(shù)漏峰。
  3. findFirst糠悼、findAny 查找操作,查找第一個(gè)浅乔、查找任何一個(gè) 返回的類型為Optional倔喂。
  4. noneMatch、allMatch靖苇、anyMatch 匹配操作席噩,數(shù)據(jù)流中是否存在符合條件的元素 返回值為bool 值。
  5. min贤壁、max 最值操作悼枢,需要自定義比較器,返回?cái)?shù)據(jù)流中最大最小的值脾拆。
  6. reduce 規(guī)約操作馒索,將整個(gè)數(shù)據(jù)流的值規(guī)約為一個(gè)值,count假丧、min双揪、max底層就是使用reduce动羽。
  7. forEach包帚、forEachOrdered 遍歷操作,這里就是對最終的數(shù)據(jù)進(jìn)行消費(fèi)了运吓。
  8. toArray 數(shù)組操作渴邦,將數(shù)據(jù)流的元素轉(zhuǎn)換成數(shù)組。

Stream的創(chuàng)建

1拘哨、通過 java.util.Collection.stream() 方法用集合創(chuàng)建流

List<String> list = Arrays.asList("a", "b", "c");
// 創(chuàng)建一個(gè)順序流
Stream<String> stream = list.stream();
// 創(chuàng)建一個(gè)并行流
Stream<String> parallelStream = list.parallelStream();

2谋梭、使用java.util.Arrays.stream(T[] array)方法用數(shù)組創(chuàng)建流

int[] array={1,3,5,6,8};
IntStream stream = Arrays.stream(array);

3、使用Stream的靜態(tài)方法:of()倦青、iterate()瓮床、generate()

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);

Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 3).limit(4);
stream2.forEach(System.out::println); // 0 3 6 9

Stream<Double> stream3 = Stream.generate(Math::random).limit(3);
stream3.forEach(System.out::println);

輸出結(jié)果:

3
6
9
0.8106623442686114
0.11554643727388458
0.1404645961428974

Process finished with exit code 0

streamparallelStream的簡單區(qū)分:

stream是順序流,由主線程按順序?qū)α鲌?zhí)行操作产镐;
parallelStream是并行流隘庄,內(nèi)部以多線程并行執(zhí)行的方式對流進(jìn)行操作,但前提是流中的數(shù)據(jù)處理沒有順序要求癣亚。

例如篩選集合中的奇數(shù)丑掺,兩者的處理不同之處:

image-20210701230623951

Stream使用

遍歷/匹配(foreach/find/match)

Stream也是支持類似集合的遍歷和匹配元素的,只是Stream中的元素是以Optional類型存在的述雾。Stream的遍歷街州、匹配非常簡單兼丰。

public class StreamTest {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
        // 遍歷輸出符合條件的元素
        list.stream().filter(x -> x > 6).forEach(System.out::println);
        // 匹配第一個(gè)
        Optional<Integer> findFirst = list.stream().filter(x -> x > 6).findFirst();
        // 匹配任意(適用于并行流)
        Optional<Integer> findAny = list.parallelStream().filter(x -> x > 6).findAny();
        // 是否包含符合特定條件的元素
        boolean anyMatch = list.stream().anyMatch(x -> x < 6);
        System.out.println("匹配第一個(gè)值:" + findFirst.get());
        System.out.println("匹配任意一個(gè)值:" + findAny.get());
        System.out.println("是否存在大于6的值:" + anyMatch);

    }
}

輸出結(jié)果:

7
9
8
匹配第一個(gè)值:7
匹配任意一個(gè)值:8
是否存在大于6的值:true

Process finished with exit code 0

篩選(filter)

篩選,是按照一定的規(guī)則校驗(yàn)流中的元素唆缴,將符合條件的元素提取到新的流中的操作鳍征。

篩選出Integer集合中大于7的元素,并打印出來

public class StreamTest {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(6, 7, 3, 8, 1, 2, 9);
        Stream<Integer> stream = list.stream();
        stream.filter(x -> x > 7).forEach(System.out::println);
    }
}

輸出結(jié)果:

8
9

Process finished with exit code 0

聚合(max/min/count)

max琐谤、min蟆技、count這些字眼你一定不陌生,沒錯(cuò)斗忌,在mysql中我們常用它們進(jìn)行數(shù)據(jù)統(tǒng)計(jì)质礼。Java stream中也引入了這些概念和用法,極大地方便了我們對集合织阳、數(shù)組的數(shù)據(jù)統(tǒng)計(jì)工作眶蕉。

案例一:獲取String集合中最長的元素。

public class StreamTest {

    public static void main(String[] args) {
        List<String> list = Arrays.asList("adnm", "admmt", "pot", "xbangd", "weoujgsd");
        Optional<String> max = list.stream().max(Comparator.comparing(String::length));
        System.out.println("最長的字符串:" + max.get());
    }
}

輸出結(jié)果:

最長的字符串:weoujgsd

Process finished with exit code 0

案例二:獲取Integer集合中的最大值唧躲。

public class StreamTest {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(7, 6, 9, 4, 11, 6);
        // 自然排序
        Optional<Integer> max = list.stream().max(Integer::compareTo);
        // 自定義排序
        Optional<Integer> max2 = list.stream().max(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1.compareTo(o2);
            }
        });
        System.out.println("自然排序的最大值:" + max.get());
        System.out.println("自定義排序的最大值:" + max2.get());
    }
}

輸出結(jié)果:

自然排序的最大值:11
自定義排序的最大值:11

Process finished with exit code 0

案例三:計(jì)算Integer集合中大于6的元素的個(gè)數(shù)造挽。

public class StreamTest {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(7, 6, 4, 8, 2, 11, 9);
        long count = list.stream().filter(x -> x > 6).count();
        System.out.println("list中大于6的元素個(gè)數(shù):" + count);
    }
}

輸出結(jié)果:

list中大于6的元素個(gè)數(shù):4

Process finished with exit code 0

映射(map/flatMap)

映射,可以將一個(gè)流的元素按照一定的映射規(guī)則映射到另一個(gè)流中弄痹。分為mapflatMap

  • map:接收一個(gè)函數(shù)作為參數(shù)饭入,該函數(shù)會(huì)被應(yīng)用到每個(gè)元素上,并將其映射成一個(gè)新的元素肛真。
  • flatMap:接收一個(gè)函數(shù)作為參數(shù)谐丢,將流中的每個(gè)值都換成另一個(gè)流,然后把所有流連接成一個(gè)流蚓让。

案例一:英文字符串?dāng)?shù)組的元素全部改為大寫乾忱。整數(shù)數(shù)組每個(gè)元素+3。

public class StreamTest {

    public static void main(String[] args) {
        String[] strArr = { "abcd", "bcdd", "defde", "fTr" };
        List<String> strList = Arrays.stream(strArr).map(String::toUpperCase).collect(Collectors.toList());
        System.out.println("每個(gè)元素大寫:" + strList);
        List<Integer> intList = Arrays.asList(1, 3, 5, 7, 9, 11);
        List<Integer> intListNew = intList.stream().map(x -> x + 3).collect(Collectors.toList());
        System.out.println("每個(gè)元素+3:" + intListNew);
    }
}

輸出結(jié)果:

每個(gè)元素大寫:[ABCD, BCDD, DEFDE, FTR]
每個(gè)元素+3:[4, 6, 8, 10, 12, 14]

Process finished with exit code 0

案例二:將兩個(gè)字符數(shù)組合并成一個(gè)新的字符數(shù)組历极。

public class StreamTest {

    public static void main(String[] args) {
        List<String> list = Arrays.asList("m,k,l,a", "1,3,5,7");
        List<String> listNew = list.stream().flatMap(s -> {
            // 將每個(gè)元素轉(zhuǎn)換成一個(gè)stream
            String[] split = s.split(",");
            Stream<String> s2 = Arrays.stream(split);
            return s2;
        }).collect(Collectors.toList());
        System.out.println("處理前的集合:" + list);
        System.out.println("處理后的集合:" + listNew);
    }
}

輸出結(jié)果:

處理前的集合:[m,k,l,a, 1,3,5,7]
處理后的集合:[m, k, l, a, 1, 3, 5, 7]

Process finished with exit code 0

歸約(reduce)

歸約窄瘟,也稱縮減,顧名思義趟卸,是把一個(gè)流縮減成一個(gè)值蹄葱,能實(shí)現(xiàn)對集合求和、求乘積和求最值操作锄列。

案例一:求Integer集合的元素之和图云、乘積和最大值。

public class StreamTest {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 3, 2, 8, 11, 4);
        // 求和方式1
        Optional<Integer> sum = list.stream().reduce(Integer::sum);
        // 求和方式2
        Optional<Integer> sum2 = list.stream().reduce(Integer::sum);
        // 求和方式3
        Integer sum3 = list.stream().reduce(0, Integer::sum);
        // 求乘積
        Optional<Integer> product = list.stream().reduce((x, y) -> x * y);
        // 求最大值方式1
        Optional<Integer> max = list.stream().reduce((x, y) -> x > y ? x : y);
        // 求最大值寫法2
        Integer max2 = list.stream().reduce(1, Integer::max);
        System.out.println("list求和:" + sum.get() + "," + sum2.get() + "," + sum3);
        System.out.println("list求積:" + product.get());
        System.out.println("list求和:" + max.get() + "," + max2);
    }
}

輸出結(jié)果:

list求和:29,29,29
list求積:2112
list求和:11,11

Process finished with exit code 0

歸集(toList/toSet/toMap)

因?yàn)榱鞑淮鎯?chǔ)數(shù)據(jù)右蕊,那么在流中的數(shù)據(jù)完成處理后琼稻,需要將流中的數(shù)據(jù)重新歸集到新的集合里。toList饶囚、toSettoMap比較常用帕翻,另外還有toCollection鸠补、toConcurrentMap等復(fù)雜一些的用法。

下面用一個(gè)案例演示toList嘀掸、toSettoMap

public class Person {

    private String name;  // 姓名
    private int salary; // 薪資
    private int age; // 年齡
    private String sex; //性別
    private String area;  // 地區(qū)

    // 構(gòu)造方法
    public Person(String name, int salary, int age,String sex,String area) {
        this.name = name;
        this.salary = salary;
        this.age = age;
        this.sex = sex;
        this.area = area;
    }

    public String getName() {
        return name;
    }

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

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getArea() {
        return area;
    }

    public void setArea(String area) {
        this.area = area;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", salary=" + salary +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", area='" + area + '\'' +
                '}';
    }
}

public class StreamTest {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 6, 3, 4, 6, 7, 9, 6, 20);
        List<Integer> listNew = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toList());
        Set<Integer> set = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toSet());
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
        Map<?, Person> map = personList.stream().filter(p -> p.getSalary() > 8000)
                .collect(Collectors.toMap(Person::getName, p -> p));
        System.out.println("toList:" + listNew);
        System.out.println("toSet:" + set);
        System.out.println("toMap:" + map);
    }
}

輸出結(jié)果:

toList:[6, 4, 6, 6, 20]
toSet:[4, 20, 6]
toMap:{Tom=Person{name='Tom', salary=8900, age=23, sex='male', area='New York'}, Anni=Person{name='Anni', salary=8200, age=24, sex='female', area='New York'}}

Process finished with exit code 0

統(tǒng)計(jì)(count/averaging)

Collectors提供了一系列用于數(shù)據(jù)統(tǒng)計(jì)的靜態(tài)方法:

  • 計(jì)數(shù):count
  • 平均值:averagingInt紫岩、averagingLongaveragingDouble
  • 最值:maxBy睬塌、minBy
  • 求和:summingInt泉蝌、summingLongsummingDouble
  • 統(tǒng)計(jì)以上所有:summarizingInt揩晴、summarizingLong勋陪、summarizingDouble

案例:統(tǒng)計(jì)員工人數(shù)、平均工資硫兰、工資總額诅愚、最高工資。

public class StreamTest {

    public static void main(String[] args) {
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        // 求總數(shù)
        long count = personList.size();
        // 求平均工資
        Double average = personList.stream().collect(Collectors.averagingDouble(Person::getSalary));
        // 求最高工資
        Optional<Integer> max = personList.stream().map(Person::getSalary).max(Integer::compare);
        // 求工資之和
        int sum = personList.stream().mapToInt(Person::getSalary).sum();
        // 一次性統(tǒng)計(jì)所有信息
        DoubleSummaryStatistics collect = personList.stream().collect(Collectors.summarizingDouble(Person::getSalary));
        System.out.println("員工總數(shù):" + count);
        System.out.println("員工平均工資:" + average);
        System.out.println("員工最高工資:" + max.get());
        System.out.println("員工工資總和:" + sum);
        System.out.println("員工工資所有統(tǒng)計(jì):" + collect);
    }
}

輸出結(jié)果:

員工總數(shù):3
員工平均工資:7900.0
員工最高工資:8900
員工工資總和:23700
員工工資所有統(tǒng)計(jì):DoubleSummaryStatistics{count=3, sum=23700.000000, min=7000.000000, average=7900.000000, max=8900.000000}

Process finished with exit code 0

分組(partitioningBy/groupingBy)

  • 分區(qū):將stream按條件分為兩個(gè)Map劫映,比如員工按薪資是否高于8000分為兩部分违孝。
  • 分組:將集合分為多個(gè)Map,比如員工按性別分組泳赋。有單級分組和多級分組雌桑。

案例:將員工按薪資是否高于8000分為兩部分;將員工按性別和地區(qū)分組

public class StreamTest {

    public static void main(String[] args) {
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "Washington"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "New York"));
        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
        // 將員工按薪資是否高于8000分組
        Map<Boolean, List<Person>> part = personList.stream().collect(Collectors.partitioningBy(x -> x.getSalary() > 8000));
        // 將員工按性別分組
        Map<String, List<Person>> group = personList.stream().collect(Collectors.groupingBy(Person::getSex));
        // 將員工先按性別分組祖今,再按地區(qū)分組
        Map<String, Map<String, List<Person>>> group2 = personList.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.groupingBy(Person::getArea)));
        System.out.println("員工按薪資是否大于8000分組情況:" + part);
        System.out.println("員工按性別分組情況:" + group);
        System.out.println("員工按性別校坑、地區(qū):" + group2);
    }
}

輸出結(jié)果:

員工按薪資是否大于8000分組情況:{false=[Person{name='Jack', salary=7000, age=25, sex='male', area='Washington'}, Person{name='Lily', salary=7800, age=21, sex='female', area='New York'}], true=[Person{name='Tom', salary=8900, age=23, sex='male', area='Washington'}, Person{name='Anni', salary=8200, age=24, sex='female', area='New York'}]}
員工按性別分組情況:{female=[Person{name='Lily', salary=7800, age=21, sex='female', area='New York'}, Person{name='Anni', salary=8200, age=24, sex='female', area='New York'}], male=[Person{name='Tom', salary=8900, age=23, sex='male', area='Washington'}, Person{name='Jack', salary=7000, age=25, sex='male', area='Washington'}]}
員工按性別、地區(qū):{female={New York=[Person{name='Lily', salary=7800, age=21, sex='female', area='New York'}, Person{name='Anni', salary=8200, age=24, sex='female', area='New York'}]}, male={Washington=[Person{name='Tom', salary=8900, age=23, sex='male', area='Washington'}, Person{name='Jack', salary=7000, age=25, sex='male', area='Washington'}]}}

Process finished with exit code 0

接合(joining)

joining可以將stream中的元素用特定的連接符(沒有的話衅鹿,則直接連接)連接成一個(gè)字符串撒踪。

public class StreamTest {

    public static void main(String[] args) {
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        String names = personList.stream().map(Person::getName).collect(Collectors.joining(","));
        System.out.println("所有員工的姓名:" + names);
        List<String> list = Arrays.asList("A", "B", "C");
        String string = list.stream().collect(Collectors.joining("-"));
        System.out.println("拼接后的字符串:" + string);
    }
}

輸出結(jié)果:

所有員工的姓名:Tom,Jack,Lily
拼接后的字符串:A-B-C

Process finished with exit code 0

排序(sorted)

sorted过咬,中間操作大渤。有兩種排序:

  • sorted():自然排序,流中元素需實(shí)現(xiàn)Comparable接口
  • sorted(Comparator com)Comparator排序器自定義排序

案例:將員工按工資由高到低(工資一樣則按年齡由大到械Ы省)排序

public class StreamTest {

    public static void main(String[] args) {
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Sherry", 9000, 24, "female", "New York"));
        personList.add(new Person("Tom", 8900, 22, "male", "Washington"));
        personList.add(new Person("Jack", 9000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 8800, 26, "male", "New York"));
        personList.add(new Person("Alisa", 9000, 26, "female", "New York"));
        // 按工資升序排序(自然排序)
        List<String> newList = personList.stream().sorted(Comparator.comparing(Person::getSalary)).map(Person::getName)
                .collect(Collectors.toList());
        // 按工資倒序排序
        List<String> newList2 = personList.stream().sorted(Comparator.comparing(Person::getSalary).reversed())
                .map(Person::getName).collect(Collectors.toList());
        // 先按工資再按年齡升序排序
        List<String> newList3 = personList.stream()
                .sorted(Comparator.comparing(Person::getSalary).thenComparing(Person::getAge)).map(Person::getName)
                .collect(Collectors.toList());
        // 先按工資再按年齡自定義排序(降序)
        List<String> newList4 = personList.stream().sorted((p1, p2) -> {
            if (p1.getSalary() == p2.getSalary()) {
                return p2.getAge() - p1.getAge();
            } else {
                return p2.getSalary() - p1.getSalary();
            }
        }).map(Person::getName).collect(Collectors.toList());
        System.out.println("按工資升序排序:" + newList);
        System.out.println("按工資降序排序:" + newList2);
        System.out.println("先按工資再按年齡升序排序:" + newList3);
        System.out.println("先按工資再按年齡自定義降序排序:" + newList4);
    }
}

輸出結(jié)果:

按工資升序排序:[Lily, Tom, Sherry, Jack, Alisa]
按工資降序排序:[Sherry, Jack, Alisa, Tom, Lily]
先按工資再按年齡升序排序:[Lily, Tom, Sherry, Jack, Alisa]
先按工資再按年齡自定義降序排序:[Alisa, Jack, Sherry, Tom, Lily]

Process finished with exit code 0

提取/組合

流也可以進(jìn)行合并泵三、去重、限制衔掸、跳過等操作烫幕。

public class StreamTest {

    public static void main(String[] args) {
        String[] arr1 = { "a", "b", "c", "d" };
        String[] arr2 = { "d", "e", "f", "g" };
        Stream<String> stream1 = Stream.of(arr1);
        Stream<String> stream2 = Stream.of(arr2);
        // concat:合并兩個(gè)流 distinct:去重
        List<String> newList = Stream.concat(stream1, stream2).distinct().collect(Collectors.toList());
        // limit:限制從流中獲得前n個(gè)數(shù)據(jù)
        List<Integer> collect = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());
        // skip:跳過前n個(gè)數(shù)據(jù)
        List<Integer> collect2 = Stream.iterate(1, x -> x + 2).skip(1).limit(5).collect(Collectors.toList());
        System.out.println("流合并:" + newList);
        System.out.println("limit:" + collect);
        System.out.println("skip:" + collect2);
    }
}

輸出結(jié)果:

流合并:[a, b, c, d, e, f, g]
limit:[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
skip:[3, 5, 7, 9, 11]

Process finished with exit code 0

分頁操作

stream api 的強(qiáng)大之處還不僅僅是對集合進(jìn)行各種組合操作,還支持分頁操作敞映。

例如较曼,將如下的數(shù)組從小到大進(jìn)行排序,排序完成之后振愿,從第1行開始捷犹,查詢10條數(shù)據(jù)出來弛饭,操作如下:

//需要查詢的數(shù)據(jù)
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5, 10, 6, 20, 30, 40, 50, 60, 100);
List<Integer> dataList = numbers.stream().sorted(Integer::compareTo).skip(0).limit(10).collect(Collectors.toList());
System.out.println(dataList.toString());

輸出結(jié)果:

[2, 2, 3, 3, 3, 5, 6, 7, 10, 20]

Process finished with exit code 0

并行操作

所謂并行,指的是多個(gè)任務(wù)在同一時(shí)間點(diǎn)發(fā)生萍歉,并由不同的cpu進(jìn)行處理侣颂,不互相搶占資源;而并發(fā)枪孩,指的是多個(gè)任務(wù)在同一時(shí)間點(diǎn)內(nèi)同時(shí)發(fā)生了憔晒,但由同一個(gè)cpu進(jìn)行處理,互相搶占資源蔑舞。

stream api 的并行操作和串行操作拒担,只有一個(gè)方法區(qū)別,其他都一樣攻询,例如下面我們使用parallelStream來輸出空字符串的數(shù)量:

List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd", "", "jkl");
// 采用并行計(jì)算方法澎蛛,獲取空字符串的數(shù)量
long count = strings.parallelStream().filter(String::isEmpty).count();
System.out.println(count);

在實(shí)際使用的時(shí)候,并行操作不一定比串行操作快蜕窿!對于簡單操作谋逻,數(shù)量非常大,同時(shí)服務(wù)器是多核的話桐经,建議使用Stream并行毁兆!反之,采用串行操作更可靠阴挣!

集合轉(zhuǎn)Map操作

在實(shí)際的開發(fā)過程中气堕,還有一個(gè)使用最頻繁的操作就是,將集合元素中某個(gè)主鍵字段作為key畔咧,元素作為value茎芭,來實(shí)現(xiàn)集合轉(zhuǎn)map的需求,這種需求在數(shù)據(jù)組裝方面使用的非常多誓沸。

public static void main(String[] args) {
    List<Person> personList = new ArrayList<>();
    personList.add(new Person("Tom",7000,25,"male","安徽"));
    personList.add(new Person("Jack",8000,30,"female","北京"));
    personList.add(new Person("Lucy",9000,40,"male","上海"));
    personList.add(new Person("Airs",10000,40,"female","深圳"));
    Map<Integer, Person> collect = personList.stream().collect(Collectors.toMap(Person::getAge, v -> v, (k1, k2) -> k1));
    System.out.println(collect);
}

輸出結(jié)果:

{40=Person{name='Lucy', salary=9000, age=40, sex='male', area='上海'}, 25=Person{name='Tom', salary=7000, age=25, sex='male', area='安徽'}, 30=Person{name='Jack', salary=8000, age=30, sex='female', area='北京'}}

Process finished with exit code 0

打開Collectors.toMap方法源碼梅桩,一起來看看。

public static <T, K, U>
    Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
                                    Function<? super T, ? extends U> valueMapper,
                                    BinaryOperator<U> mergeFunction) {
    return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
}

從參數(shù)表可以看出:

  • 第一個(gè)參數(shù):表示 key
  • 第二個(gè)參數(shù):表示 value
  • 第三個(gè)參數(shù):表示某種規(guī)則

上文中的Collectors.toMap(Person::getAge, v -> v, (k1,k2) -> k1)拜隧,表達(dá)的意思就是將age的內(nèi)容作為key宿百,v -> v是表示將元素person作為value,其中(k1,k2) -> k1表示如果存在相同的key洪添,將第一個(gè)匹配的元素作為內(nèi)容垦页,第二個(gè)舍棄!

標(biāo)簽: [Java]


在這里插入圖片描述
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末干奢,一起剝皮案震驚了整個(gè)濱河市痊焊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖薄啥,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件貌矿,死亡現(xiàn)場離奇詭異,居然都是意外死亡罪佳,警方通過查閱死者的電腦和手機(jī)逛漫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赘艳,“玉大人酌毡,你說我怎么就攤上這事±俟埽” “怎么了枷踏?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長掰曾。 經(jīng)常有香客問我旭蠕,道長,這世上最難降的妖魔是什么旷坦? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任掏熬,我火速辦了婚禮,結(jié)果婚禮上秒梅,老公的妹妹穿的比我還像新娘旗芬。我一直安慰自己,他們只是感情好捆蜀,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布疮丛。 她就那樣靜靜地躺著,像睡著了一般辆它。 火紅的嫁衣襯著肌膚如雪誊薄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天锰茉,我揣著相機(jī)與錄音呢蔫,去河邊找鬼。 笑死洞辣,一個(gè)胖子當(dāng)著我的面吹牛咐刨,可吹牛的內(nèi)容都是我干的昙衅。 我是一名探鬼主播扬霜,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼而涉!你這毒婦竟也來了著瓶?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤啼县,失蹤者是張志新(化名)和其女友劉穎材原,沒想到半個(gè)月后沸久,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡余蟹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年卷胯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片威酒。...
    茶點(diǎn)故事閱讀 39,965評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡窑睁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出葵孤,到底是詐尸還是另有隱情担钮,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布尤仍,位于F島的核電站箫津,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏宰啦。R本人自食惡果不足惜苏遥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望赡模。 院中可真熱鬧暖眼,春花似錦、人聲如沸纺裁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽欺缘。三九已至栋豫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間谚殊,已是汗流浹背丧鸯。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嫩絮,地道東北人丛肢。 一個(gè)月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像剿干,于是被迫代替她去往敵國和親蜂怎。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容