Java1.8新特性之Stream的使用

                                            JAVA1.8新特性Stream API的簡單使用

list.stream()和list.parallelStream()的區別:stream(),單線程操作,雖然Stream API支持多線程操作集合,但是普通的stream()並沒有提供多線程操作,實質上還是串行運行,對於遍歷集合來說,它和迭代器,for循環相同,效率沒有太大的區別。而parallelStream()則是多線程操作list,它會把list拆分成幾部分,使用多線程並行操作,而至於會產生多少個線程來處理,則和cpu個數有關,一般線程個數和cpu個數相同,但是可以通過參數設置。下面就列舉一些常見的Stream操作集合的例子。

目錄

前提條件:

一、forEach()

二、map()函數

三、Collectors.grouping()函數的使用

四、filter()函數


前提條件:

測試用Person類:

public class Person
{
    private String name;

    private Integer age;

    private String gender;

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }
}

 測試用Man類,該類是Person類子類,擁有自己特別的屬性target。

public class Man extends Person {
    private String target;

    public String getTarget() {
        return target;
    }

    public void setTarget(String target) {
        this.target = target;
    }
}

 Main函數代碼:

        List<Person> persons = new LinkedList();
        Person person1 = new Person();
        Person person2 = new Person();
        Person person3 = new Person();
        Person person4 = new Person();
        person1.setName("張三");
        person1.setAge(24);
        person1.setGender("男");
        person2.setName("李四");
        person2.setAge(44);
        person2.setGender("男");
        person3.setName("王五");
        person3.setAge(34);
        person3.setGender("女");
        person4.setName("小二");
        person4.setAge(14);
        person4.setGender("男");
        persons.add(person1);
        persons.add(person2);
        persons.add(person3);
        persons.add(person4);

一、forEach()

該函數和for循環相同,主要作用就是遍歷集合裏面的所有元素,沒有返回值。

persons.parallelStream().forEach(person -> System.out.println(person.getName() + "--" + person.getAge() + "--" + person.getGender()));

運行結果:

二、map()

該函數用於轉換集合裏面的元素,比如,將Person類里名字提取出來組成一個List<String>類型,這個時候就做了一個轉換:List<Person>→List<String>,代碼如下:

        List<String> personNames = persons.parallelStream().map(person -> person.getName()).collect(Collectors.toList());
        personNames.parallelStream().forEach(System.out::println);

運行結果:

map()函數裏面的lambda表達式需要有一個返回值,返回值的類型就是你最後想要轉換成的類型。如果你想轉換成Integer的List,那麼你就只需要返回person.getAge()。

當lambda表示足夠簡單,就不需要寫return關鍵字來返回自己需要的類型,如果邏輯比較複雜,需要2條以上的語句才能返回想要的結果,那麼就需要寫return語句,比如,現在我把person類集合轉換爲其子類man類的集合:

        List<Man> mans = persons.parallelStream().map(person -> {
            Man oneMan = new Man();
            oneMan.setName(person.getName());
            oneMan.setTarget("我是子類Man,我的名字是" + person.getName());
            return oneMan;
        }).collect(Collectors.toList());
        mans.parallelStream().forEach(man -> System.out.println(man.getName() + "--" + man.getTarget()));

運行結果:

另外說一句,Collectors類可以轉換如下集中集合:List、Set、Map,不僅僅只有toList()方法,其中還有很多其他方法,比如分組groupingBy,統計counting等,非常實用,下面就舉一個通過grouping函數,將list轉換爲map的例子。

三、Collectors.groupingBy()

現在將persons集合轉換成,以person名字爲鍵,person類的集合爲值的map<String,List<Person>>,代碼如下:

        Map<String, List<Person>> nameToPersons = persons.parallelStream().collect(Collectors.groupingBy(Person::getName));

        for (Map.Entry<String, List<Person>> nameToPerson : nameToPersons.entrySet()) {
            System.out.println("鍵:" + nameToPerson.getKey() + "--值:" + nameToPerson.getValue().get(0).getName() + "--" +  nameToPerson.getValue().get(0).getAge());
        }

運行結果:

這裏解釋一下爲什麼值是List類型,因爲該方法不能確定你的鍵是否唯一,對應的,有另外的方法應對鍵唯一的情況,下面我用person的名字和年齡作爲鍵,person作爲值。(用什麼作爲鍵,和值都是隨意改變的)

        Map<String, Person> nameToPersons = persons.parallelStream().collect(Collectors.toMap(person -> person.getName() + person.getAge(), person -> person));

        for (Map.Entry<String, Person> nameToPerson : nameToPersons.entrySet()) {
            System.out.println("鍵:" + nameToPerson.getKey() + "--值:" + nameToPerson.getValue().getName() + "--" +  nameToPerson.getValue().getAge());
        }

運行結果:

四、filter()

該函數用作過濾,過濾掉集合中不滿足條件的值,保留需要的值。該函數接收一個boolean類型的表達式,如果集合中的值使這個表達式爲true,那麼就保留,否則移出,當然,如果你沒用使用集合中的值參與表達式,那麼這個集合最終保留的值取決於表達式。現在我過濾掉persons裏面年齡小於40的值,最後結果應該只會留下李四,44歲這個值,代碼如下:

        List<Person> ageGt40s = persons.parallelStream().filter(person -> person.getAge() > 40).collect(Collectors.toList());
        ageGt40s.forEach(person -> System.out.println(person.getName() + "--" + person.getAge() + "--" + person.getGender()));

運行結果如下:

最後再說一下,關於使用parallelStream操作集合,必須考慮多線程的影響,因爲其實質是使用多線程同時操作集合的,如果在操作中,對外部變量進行了修改操作,那麼需要考慮是否線程安全問題。這裏貼上全部main函數測試代碼:

public class TestUnitl
{
    public static void main(String[] args)
    {
        List<Person> persons = new LinkedList();
        Person person1 = new Person();
        Person person2 = new Person();
        Person person3 = new Person();
        Person person4 = new Person();
        person1.setName("張三");
        person1.setAge(24);
        person1.setGender("男");
        person2.setName("李四");
        person2.setAge(44);
        person2.setGender("男");
        person3.setName("王五");
        person3.setAge(34);
        person3.setGender("女");
        person4.setName("小二");
        person4.setAge(14);
        person4.setGender("男");
        persons.add(person1);
        persons.add(person2);
        persons.add(person3);
        persons.add(person4);

        //persons.parallelStream().forEach(person -> System.out.println(person.getName() + "--" + person.getAge() + "--" + person.getGender()));
        //List<String> personNames = persons.parallelStream().map(person -> person.getName()).collect(Collectors.toList());
        //personNames.parallelStream().forEach(System.out::println);
        //List<Man> mans = persons.parallelStream().map(person -> {
        //    Man oneMan = new Man();
        //    oneMan.setName(person.getName());
        //    oneMan.setTarget("我是子類Man,我的名字是" + person.getName());
        //    return oneMan;
        //}).collect(Collectors.toList());
        //mans.parallelStream().forEach(man -> System.out.println(man.getName() + "--" + man.getTarget()));
        //Map<String, List<Person>> nameToPersons = persons.parallelStream().collect(Collectors.groupingBy(Person::getName));
        //
        //for (Map.Entry<String, List<Person>> nameToPerson : nameToPersons.entrySet()) {
        //    System.out.println("鍵:" + nameToPerson.getKey() + "--值:" + nameToPerson.getValue().get(0).getName() + "--" +  nameToPerson.getValue().get(0).getAge());
        //}
        //Map<String, Person> nameToPersons = persons.parallelStream().collect(Collectors.toMap(person -> person.getName() + person.getAge(), person -> person));
        //
        //for (Map.Entry<String, Person> nameToPerson : nameToPersons.entrySet()) {
        //    System.out.println("鍵:" + nameToPerson.getKey() + "--值:" + nameToPerson.getValue().getName() + "--" +  nameToPerson.getValue().getAge());
        //}
        List<Person> ageGt40s = persons.parallelStream().filter(person -> person.getAge() > 40).collect(Collectors.toList());
        ageGt40s.forEach(person -> System.out.println(person.getName() + "--" + person.getAge() + "--" + person.getGender()));
    }

}

目前筆者比較常用的就這四個函數,上面也總結了淺顯的使用方式,爲了更加深入瞭解stream api,筆者也會繼續學習瞭解該技術。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章