1. 程式人生 > >java8新特性Stream的基本使用

java8新特性Stream的基本使用

 第一、建立Stream

     1.1、根據集合的建立

        List<String> list = new ArrayList<>();
        Collections.addAll(list,"aa","aaa","Aaaa","b");//這段程式碼相當於使用了4次list.add
        Stream<String> stream = list.stream();

     1.2、根據陣列建立

        //String陣列
        String[] strings = {"aa","aaa","Aaaa","b"};
        Stream<String> stringStream=Arrays.stream(strings);//第一種
        Stream<String> stringStream1=Stream.of(strings);//第二種
        //Integer陣列
        Integer[] integers = {1,2,3,4,5,6};
        Stream<Integer> integerStream=Arrays.stream(integers);
        Stream<Integer> integerStream1=Stream.of(integers);//第二種

(下面兩種建立方式不理解的話,先看完流操作,再回來看看) 

1.3、根據檔案建立

    public static void main(String[] args) throws FileNotFoundException {
        AtomicLong lineCount = new AtomicLong(0);//計算檔案的行數
        List<String> stringList = new ArrayList<>();//用於接收讀取的每一行內容,當size為一萬時,存入資料庫,然後清空

        File file = new File(ResourceUtils.getURL("classpath:"+"test.txt").getPath());//我這是SpringBoot專案,此方法是獲取resources目錄下的test.txt,如果test.txt在resources\static下可以寫成"classpath:"+"static/test.txt"
//        File file = new File("F:"+File.separator+"test.txt");//也可以這樣寫,檔案存在F:\test.txt
        if (!file.exists()){
            System.out.println("檔案不存在!!");
            return;
        }
            
        System.out.println(file.getPath());
        try(BufferedReader reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)) {
            Stream<String> stringStream=reader.lines();//得到流
            stringStream.forEach(line -> {
                System.out.println(line);
                stringList.add(line);
                lineCount.addAndGet(1);

                if (lineCount.longValue()%10000==0){
                    //當stringList裡有一萬條資料時存一次資料庫,然後清空stringList,這對於大小上G的檔案讀取很有用
                }
            });
        }catch (IOException e) {
            e.printStackTrace();
        }

    }

 1.4、根據函式建立無限流

   Stream API提供了兩個靜態方法建立無限流:Stream.iterate和Stream.generate,這樣流的元素是無窮無盡的,一般用limit加以控制

         1.4.1 Stream.iterate

Stream.iterate(0,n->n+2).limit(10).forEach(System.out::println);//將會從0開始列印十個雙數

         1.4.2 Stream.generate

Stream.generate(Math::random).limit(10).forEach(System.out::println);//輸出十個0-1的隨機雙精度數

第二、流操作

      流操作分為:中間操作和終端操作

      中間操作會返回另外一個流,而終端操作則會返回一個非流的結果如Integer、List、void(根據返回結果很容易區分是中間操作還是終端操作了)

      因為中間操作返回的是一個流,所以還可以呼叫其他的中間操作,看起來就像是一條流水線,直到執行終端操作

      利用程式碼來看一下,filter和map是中間操作,collect是終端操作

        List<String> list = new ArrayList<>();
        Collections.addAll(list,"aa","aaa","Aaaa","b");
        List<String> list1=list.stream().filter(s -> s.contains("a")).map(s -> s.toLowerCase()).collect(toList());
        //上面這段程式碼就像是:過濾出只包含“a”的內容->將上一次得到的內容轉成小寫->返回一個List的結果
        //list1得到的資料是:[aa, aaa, aaaa]

   下面我們會一一介紹中間操作和終端操作,並且都會有一個簡單的使用的例子

   2.1 filter(中間操作)

//我們先看一下filter方法接收什麼、下面是java.util.stream包下的Stream介面中的方法,這個filter到底是具體怎麼實現的我們不需要管,我只需要知道這個方法接收什麼型別的引數和返回時什麼型別的引數。
//這裡接收的是一個函式式介面Predicate(函式式介面:介面中有且只有一個抽象方法,他就是函式式介面,但是他可以有多個預設方法和靜態方法),返回的是Stream返回的還是流所以filter是一箇中間操作。
Stream<T> filter(Predicate<? super T> predicate);

Stream涉及到許多函式式介面,所以我建議在學習Stream之前先學習一下java8的另外一個新特性Lambda.

Predicate中的抽象方法test接收的是泛型T,返回一個boolean值。

所以filter操作會接受一個返回boolean的函式作為引數,並且返回一個包含所有符合條件(函式返回true)的新流。

        List<String> list = new ArrayList<>();
        Collections.addAll(list,"aa","aaa","Aaaa","b");
        List<String> list1=list.stream().filter(s -> s.length()>2).collect(toList());

這裡我們從list中篩選出長度大於2的,符合條件的是“aaa”和“Aaaa”.

2.2 distinct(中間操作)

用於去除重複的內容

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Collections.addAll(list,"aaa","aaa","Aaaa","b");//有兩個相同的“aaa”
        List<String> list1=list.stream().filter(s -> s.length()>2).distinct().collect(toList());//配合filter使用
        System.out.println(list1);//列印[aaa, Aaaa],//去除重複的“aaa”,只保留一個
    }

2.3 limit(中間操作)

返回一個不超過給定長度的流。如果流是有序的如Stream<List>會返回前n個元素的新流,也可用於無序如Set.

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Collections.addAll(list,"aaa","aaa","Aaaa","b");
        List<String> list1=list.stream().limit(3).collect(toList());//只取3個元素,去掉後面的
        System.out.println(list1);//[aaa, aaa, Aaaa]
    }

2.4 skip(中間操作)

跳過給定的n個元素,下如跳過n=2個元素,如果n>=list.size這會返回一個空流

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Collections.addAll(list,"aaa","aaa","Aaaa","b");
        List<String> list1=list.stream().skip(2).collect(toList());//跳過前面2個元素,只取後面的
        System.out.println(list1);//[Aaaa, b]
    }

2.5 map(中間操作)

map會接收一個函式作為引數,如下程式碼中s.leng()返回Integer,所以map返回的新流是Stream<Integer>,然後我們toList()將其轉化為集合List<Integer>

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Collections.addAll(list,"aaa","aaa","Aaaa","b");
        List<Integer> list1=list.stream().map(s -> s.length()).collect(toList());//配合filter使用
        System.out.println(list1);//[3, 3, 4, 1]
    }

 我們也可以用於處理物件的集合,從中取出一個屬性作為集合,相當mysql中從一個表裡取出中一列資料

public class User {
    private String name;
    private int age;
    public User() {
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
//get、set、toString...
}

 下面我們取出所有的姓名name,因為user.getName()返回的是String所有map返回的新流是Stream<String>,toList()之後返回的是List<String>

    public static void main(String[] args) {
        List<User> list2 = new ArrayList<>();
        Collections.addAll(list2,new User("張三",25),new User("李四",26),new User("王五",28));
        List<String> list3 = list2.stream().map(user -> user.getName()).collect(toList());
        System.out.println(list3);//[張三, 李四, 王五]
    }

2.6 flatMap(中間操作)

將流中的每個值都換成另一個流,然後把所有流連線成一個流。

    public static void main(String[] args) {
        List<String> list4 = new ArrayList<>();
        Collections.addAll(list4,"hello","world");
        List<String> stream=list4.stream().map(s -> s.split("")).flatMap(strings -> Arrays.stream(strings)).distinct().collect(toList());
        System.out.println(stream);//[h, e, l, o, w, r, d]
    }

2.7 anyMatch(終端操作)

anyMatch就像是:流中至少有一個元素能匹配,anyMatch返回的是一個boolean值,所以不是中間操作而是終端操作

    public static void main(String[] args) {
        List<User> list2 = new ArrayList<>();
        Collections.addAll(list2,new User("張三",25),new User("李四",26),new User("王五",28));
        if(list2.stream().anyMatch(user -> user.getName().equals("李四"))){
            System.out.println("這個集合中是有一個叫做李四的人");//將會打印出
        }
    }

2.8 allMatch(終端操作)

allMatch就像是:流中是否所有元素都匹配

    public static void main(String[] args) {
        List<User> list2 = new ArrayList<>();
        Collections.addAll(list2,new User("張三",25),new User("李四",26),new User("王五",28));
        if(list2.stream().allMatch(user -> user.getAge()==25)){
            System.out.println("這個集合中的人都是25歲");//不會列印,需要所以的年齡都是25,可以自行更改驗證
        }
    }

2.9 noneMatch(終端操作)

noneMatch就像是:流中是否所有元素都不匹配。noneMatch和allMatch是相對的。noneMatch是所有都不匹配才是true,而allMatch所有都匹配才是true

    public static void main(String[] args) {
        List<User> list2 = new ArrayList<>();
        Collections.addAll(list2,new User("張三",25),new User("李四",26),new User("王五",28));
        if(list2.stream().noneMatch(user -> user.getName().equals("趙六"))){
            System.out.println("將會列印輸出,因為集合中沒有叫做趙六的");//所有都不匹配,列印
        }
    }

2.10 findAny(終端操作)

獲取任一元素,返回流中任意一個元素,返回的是Optional而不是流,所以也是終端操作。Optional是java8新引入的容器類,代表一個值存在或者不存在,用來處理值為null的問題。(注意findAny返回的只有一個元素,和我們下面將要將的findFirst一樣只返回一個)

    public static void main(String[] args) {
        List<User> list2 = new ArrayList<>();
        Collections.addAll(list2,new User("張三",25),new User("李四",26),new User("王五",28));
        Optional<User> optional= list2.stream().filter(user -> user.getAge()<=30).findAny();
        if (optional.isPresent()){
            System.out.println("optional有值我將會列印輸出");
        }
        optional.ifPresent(user -> System.out.println(user.toString()));
        User u = optional.get();
        User u2 = optional.orElse(new User("趙六",30));
    }

Optional操作也很簡單,有幾個比較常用的方法:

boolean isPresent():將在Optional包含值的時候返回true,否則返回false

void ifPresent(Consumer<? super T> consumer):會在值存在的時候執行給定的程式碼塊

T get():會在值存在時返回值,否則丟擲NoSuchElementException異常

T orElse(T other):值存在時返回值,否則返回預設值other

2.11 findFirst(終端操作)

findFirst與findAny類似也是返回一個元素Optional,findFirst用於有序的流中如Stream<List>等

    public static void main(String[] args) {
        List<User> list2 = new ArrayList<>();
        Collections.addAll(list2,new User("張三",25),new User("李四",26),new User("王五",28));
        Optional<User> optional= list2.stream().filter(user -> user.getAge()<=30).findFirst();
    }

2.12 reduce(終端操作)

reduce多用於處理數值,如求最大值、最小值、求和等

如下求和,有六種方法其結果都是一樣的。

    public static void main(String[] args) {
        List<Integer> list3 = new ArrayList<>();
        Collections.addAll(list3,4,7,5,6);
        int sum = list3.stream().reduce(0,(a,b)->a+b);
        int sum1 = list3.stream().reduce(0,(a,b)->Integer.sum(a,b));
        int sum2 = list3.stream().reduce(0,Integer::sum);
        Optional<Integer> optional = list3.stream().reduce((a,b)->a+b);
        Optional<Integer> optional1 = list3.stream().reduce((a,b)->Integer.sum(a,b));
        Optional<Integer> optional2 = list3.stream().reduce(Integer::sum);

        System.out.println("總和為:"+sum);//總和為:22
        System.out.println("總和為:"+sum1);//總和為:22
        System.out.println("總和為:"+sum2);//總和為:22
        optional.ifPresent(sum3->System.out.println("總和為:"+sum3));
        optional1.ifPresent(sum4->System.out.println("總和為:"+sum4));
        optional2.ifPresent(sum5->System.out.println("總和為:"+sum5));
    }

理解了上面的六種方法之後,求最大值、最小值方法類似的如:求最大值(記得將reduce方法的第一個引數設定為流中包含的元素)

        int max = list3.stream().reduce(4,(a,b)->a>=b?a:b);
        int max1 = list3.stream().reduce(4,(a,b)->Integer.max(a,b));
        int max2 = list3.stream().reduce(4,Integer::max);
        System.out.println("最大值:"+max);
        System.out.println("最大值:"+max1);
        System.out.println("最大值:"+max2);

當然也可以設定得到的結果是Optional容器類(這個還不需要考慮如何填寫第一個引數)

        Optional<Integer> optional3 = list3.stream().reduce((a,b)->a>=b?a:b);
        Optional<Integer> optional4 = list3.stream().reduce((a,b)->Integer.max(a,b));
        Optional<Integer> optional5 = list3.stream().reduce(Integer::max);

2.13 sorted(中間操作)

sorted處理List<String>

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Collections.addAll(list,"aaa","aaa","Aaaa","b");
        List<String> list1=list.stream().sorted(Comparator.comparing(String::length)).collect(toList());
        System.out.println(list1);//[b, aaa, aaa, Aaaa]
        List<String> list2=list.stream().sorted(Comparator.comparing(String::length).reversed()).collect(toList());
        System.out.println(list2);//[Aaaa, aaa, aaa, b]

sorted處理List<Integer>

    public static void main(String[] args) {
        List<Integer> list3 = new ArrayList<>();
        Collections.addAll(list3,4,7,5,6);

        List<Integer> integerList = list3.stream().sorted((a,b)->a.compareTo(b)).collect(toList());
        System.out.println(integerList);//[4, 5, 6, 7]
        List<Integer> integerList2 = list3.stream().sorted((a,b)->b.compareTo(a)).collect(toList());
        System.out.println(integerList2);//[7, 6, 5, 4]
    }

sorted處理List<User>

    public static void main(String[] args) {
        List<User> list4 = new ArrayList<>();
        Collections.addAll(list4,new User("張三",27),new User("李四",27),new User("王五",28),new User("趙六",25));

        List<User> userList=list4.stream().sorted(Comparator.comparing(User::getAge).thenComparing(User::getName)).collect(toList());//先按年齡從小到大排序,如果年齡相同者根據名字從小到大排序
        System.out.println(userList);//[User{name='趙六', age=25}, User{name='張三', age=27}, User{name='李四', age=27}, User{name='王五', age=28}]
        List<User> userList2=list4.stream().sorted(Comparator.comparing(User::getAge).reversed()).collect(toList());
        System.out.println(userList2);//[User{name='王五', age=28}, User{name='張三', age=27}, User{name='李四', age=27}, User{name='趙六', age=25}]
    }

2.14 count(終端操作)

計算元素的個數

        List<Integer> list3 = new ArrayList<>();
        Collections.addAll(list3,4,7,5,6);
        long count = list3.stream().count();//count=4

第三、收集器(收集器也屬於流操作,不過內容太多,就分開寫了)

 收集器之前我們也用過也就是collect(toList()),不過我們都是按順序將流中的元素生成一個列表,收集器的功能可不是僅僅這樣,下面將介紹更多。

3.1 counting統計

    public static void main(String[] args){
        List<User> list4 = new ArrayList<>();
        Collections.addAll(list4,new User("張三",27),new User("李四",27),new User("王五",28),new User("趙六",25));

        long count = list4.stream().collect(Collectors.counting());//統計
        System.out.println(count);//4
        long count2 = list4.stream().count();//兩種方法結果一樣
        System.out.println(count);//4
    }

3.2 maxBy、minBy  最大值和最小值

 

    public static void main(String[] args){  
        List<User> list4 = new ArrayList<>();
        Collections.addAll(list4,new User("張三",27),new User("李四",27),new User("王五",28),new User("趙六",25));

        Optional<User> optional = list4.stream().collect(Collectors.maxBy(Comparator.comparingInt(User::getAge)));//求年齡最大的
        optional.ifPresent(System.out::println);//User{name='王五', age=28}

        Optional<User> optional2 = list4.stream().collect(Collectors.minBy(Comparator.comparingInt(User::getAge)));//求年齡最小的
        optional2.ifPresent(System.out::println);//User{name='趙六', age=25}
    }

3.3 averaging平均值

 

    public static void main(String[] args){
        List<User> list4 = new ArrayList<>();
        Collections.addAll(list4,new User("張三",27),new User("李四",27),new User("王五",28),new User("趙六",25));

        Double ageAverage=list4.stream().collect(Collectors.averagingInt(User::getAge));//求年齡的平均值
        System.out.println(ageAverage);//26.75
    }

3.4  summing 求和

    public static void main(String[] args){
        List<User> list4 = new ArrayList<>();
        Collections.addAll(list4,new User("張三",27),new User("李四",27),new User("王五",28),new User("趙六",25));

        Integer sum=list4.stream().collect(Collectors.summingInt(User::getAge));
        System.out.println(sum);//107

    }

3.5 只需要一步就可以求出最大最小值、和、平均值、統計

    public static void main(String[] args){
        List<User> list4 = new ArrayList<>();
        Collections.addAll(list4,new User("張三",27),new User("李四",27),new User("王五",28),new User("趙六",25));

        IntSummaryStatistics intSummaryStatistics=list4.stream().collect(Collectors.summarizingInt(User::getAge));
        System.out.println(intSummaryStatistics);//IntSummaryStatistics{count=4, sum=107, min=25, average=26.750000, max=28}

    }

 3.6 joining 連線字串

    public static void main(String[] args){
        List<User> list4 = new ArrayList<>();
        Collections.addAll(list4,new User("張三",27),new User("李四",27),new User("王五",28),new User("趙六",25));

        String joinName = list4.stream().map(User::getName).collect(Collectors.joining());//只提取名字進行連線
        System.out.println(joinName);//張三李四王五趙六
        String joinName2 = list4.stream().map(User::getName).collect(Collectors.joining(", "));//只提取名字進行連線,名字之間用", "分開
        System.out.println(joinName2);//張三, 李四, 王五, 趙六
        String joinName3 = list4.stream().map(User::getName).collect(Collectors.joining(", " , "[" , "]"));//只提取名字進行連線,名字之間用", "分開,並且加上字首“[”和字尾“]”
        System.out.println(joinName3);//[張三, 李四, 王五, 趙六]


        String joinStr = list4.stream().map(User::toString).collect(Collectors.joining());//輸出全部資訊
        System.out.println(joinStr);//User{name='張三', age=27}User{name='李四', age=27}User{name='王五', age=28}User{name='趙六', age=25}
    }

3.7 groupingBy 分組

    public static void main(String[] args){
        Collections.addAll(list4,new User("張三",27),new User("李四",27),new User("王五",28),new User("趙六",25));

        Map<Integer,List<User>> map=list4.stream().collect(Collectors.groupingBy(User::getAge));//根據年齡分組
        System.out.println(map);//{25=[User{name='趙六', age=25}], 27=[User{name='張三', age=27}, User{name='李四', age=27}], 28=[User{name='王五', age=28}]}

        //上面是根據年齡的某一值分類,現在我們想要按範圍分類,age<=25的則分類為"小鮮肉",25<age<28的分類"花樣年華",28=<age的為"老臘肉"
        Map<String,List<User>> map2 = list4.stream().collect(Collectors.groupingBy(user->{
            int age = user.getAge();
            if (age<=25){
                return "小鮮肉";
            }else if (age>25&&age<28){
                return "花樣年華";
            }else {
                return "老臘肉";
            }
        }));
        System.out.println(map2);//{老臘肉=[User{name='王五', age=28}], 小鮮肉=[User{name='趙六', age=25}], 花樣年華=[User{name='張三', age=27}, User{name='李四', age=27}]}

    }

   多級分組

  為了更好的進行多級分組,我們在User類上加了個sex性別屬性。

(注意看:第二個groupingBy是第一個groupingBy方法的引數,所以我們還可以在第二個groupingBy方法上繼續加達到更多級的分組)

:先按照年齡再按照性別

    public static void main(String[] args){

        List<User> list4 = new ArrayList<>();
        Collections.addAll(list4,new User("張三",27,"男"),new User("李四",27,"女"),new User("王五",28,"男"),new User("趙六",25,"男"),new User("錢七",28,"男"));

        //先按照年齡範圍分組,同一分組中再按照年齡分組
        Map<String, Map<String, List<User>>> map2 = list4.stream().collect(Collectors.groupingBy(user->{
            int age = user.getAge();
            if (age<=25){
                return "小鮮肉";
            }else if (age>25&&age<28){
                return "花樣年華";
            }else {
                return "老臘肉";
            }
        },Collectors.groupingBy(User::getSex)));
        System.out.println(map2);//{老臘肉={男=[User{name='王五', age=28, sex='男'}, User{name='錢七', age=28, sex='男'}]}, 小鮮肉={男=[User{name='趙六', age=25, sex='男'}]}, 花樣年華={女=[User{name='李四', age=27, sex='女'}], 男=[User{name='張三', age=27, sex='男'}]}}
    }

:先按照性別再按照年齡

    public static void main(String[] args){
        List<User> list4 = new ArrayList<>();
        Collections.addAll(list4,new User("張三",27,"男"),new User("李四",27,"女"),new User("王五",28,"男"),new User("趙六",25,"男"),new User("錢七",28,"男"));

        Map<String, Map<String, List<User>>> map2 = list4.stream().collect(Collectors.groupingBy(User::getSex,Collectors.groupingBy(user->{
            int age = user.getAge();
            if (age<=25){
                return "小鮮肉";
            }else if (age>25&&age<28){
                return "花樣年華";
            }else {
                return "老臘肉";
            }
        })));
        System.out.println(map2);//{女={花樣年華=[User{name='李四', age=27, sex='女'}]}, 男={老臘肉=[User{name='王五', age=28, sex='男'}, User{name='錢七', age=28, sex='男'}], 小鮮肉=[User{name='趙六', age=25, sex='男'}], 花樣年華=[User{name='張三', age=27, sex='男'}]}}
    }

 :我們也可以對子組進行統計或其他操作

    public static void main(String[] args){
        List<User> list4 = new ArrayList<>();
        Collections.addAll(list4,new User("張三",27,"男"),new User("李四",27,"女"),new User("王五",28,"男"),new User("趙六",25,"男"),new User("錢七",28,"男"));

        Map<String, List<User>> map=list4.stream().collect(Collectors.groupingBy(User::getSex));//根據性別進行一級分組
        System.out.println(map);//{女=[User{name='李四', age=27, sex='女'}], 男=[User{name='張三', age=27, sex='男'}, User{name='王五', age=28, sex='男'}, User{name='趙六', age=25, sex='男'}, User{name='錢七', age=28, sex='男'}]}

        Map<String, Long> map2 = list4.stream().collect(Collectors.groupingBy(User::getSex,Collectors.counting()));
        System.out.println(map2);//{女=1, 男=4}
    }
    public static void main(String[] args){
        List<User> list4 = new ArrayList<>();
        Collections.addAll(list4,new User("張三",27,"男"),new User("李四",27,"女"),new User("王五",28,"男"),new User("趙六",25,"男"),new User("錢七",28,"男"));
        //先按照性別分組,再在子組中選出年齡最大的一個
        Map<String, User> map2 = list4.stream().collect(Collectors.groupingBy(User::getSex,collectingAndThen(maxBy(Comparator.comparingInt(User::getAge)),Optional::get)));
        System.out.println(map2);//{女=User{name='李四', age=27, sex='女'}, 男=User{name='王五', age=28, sex='男'}}
    }

 3.8 partitioningBy 分割槽

 為了更好的觀察分組的功能,先在User類中增加下面一個方法isMan,如果是男的返回true,否則返回false.

    public boolean isMan(){
        return this.getSex().equals("男")?true:false;
    }

 partitioningBy分割槽其實和groupingBy分組幾乎一樣,區別在於partitioningBy方法第一個引數是一個返回boolean值的函式,得到的結果Map鍵是boolean,所以 partitioningBy分割槽只有兩種結果,true或者false.

    public static void main(String[] args){
        List<User> list4 = new ArrayList<>();
        Collections.addAll(list4,new User("張三",27,"男"),new User("李四",27,"女"),new User("王五",28,"男"),new User("趙六",25,"男"),new User("錢七",28,"男"));

        Map<Boolean, List<User>> map=list4.stream().collect(Collectors.partitioningBy(User::isMan));
        System.out.println(map);//{false=[User{name='李四', age=27, sex='女'}], true=[User{name='張三', age=27, sex='男'}, User{name='王五', age=28, sex='男'}, User{name='趙六', age=25, sex='男'}, User{name='錢七', age=28, sex='男'}]}
        //分割槽和分組一樣可以對子組進行一些其他操作
        Map<Boolean, Long> map2 = list4.stream().collect(Collectors.partitioningBy(User::isMan,counting()));
        System.out.println(map2);//{false=1, true=4}
    }