Stream閃亮登場

Stream閃亮登場

一. Stream(流)是什麼,幹什麼

Stream是一類用於替代對集合操做的工具類+Lambda式編程,他能夠替代現有的遍歷、過濾、求和、求最值、排序、轉換等

二. Stream操做方式

  • 並行方式parallelStream
  • 順序方式Stream

三. Stream優點

  • Lambda 可有效減小冗餘代碼,減小開發工做量
  • 內置對集合List、Map的多種操做方式,含基本數據類型處理
  • 並行Stream有效率優點(內置多線程)

四. Stream(流)的基本使用

  • 遍歷forEach
@Test
    public void stream() {
        //操做List
        List<Map<String, String>> mapList = new ArrayList() {
            {
                Map<String, String> m = new HashMap();
                m.put("a", "1");
                Map<String, String> m2 = new HashMap();
                m2.put("b", "2");
                add(m);
                add(m2);
            }
        };
        mapList.stream().forEach(item-> System.out.println(item));

        //操做Map
        Map<String,Object> mp = new HashMap(){
            {
                put("a","1");
                put("b","2");
                put("c","3");
                put("d","4");
            }
        };
        mp.keySet().stream().forEachOrdered(item-> System.out.println(mp.get(item)));
    }
  • 過濾filter
List<Integer> mapList = new ArrayList() {
          {
              add(1);
              add(10);
              add(12);
              add(33);
              add(99);
          }
      };
     //mapList.stream().forEach(item-> System.out.println(item));
      mapList = mapList.stream().filter(item->{
          return item>30;
      }).collect(Collectors.toList());
      System.out.println(mapList);
  • 轉換map和極值編程

    @Test
      public void trans(){
          List<Person> ps = new ArrayList<Person>(){
              {
                  Person p1 = new Person();
                  p1.setAge(11);
                  p1.setName("張強");
    
                  Person p2 = new Person();
                  p2.setAge(17);
                  p2.setName("李思");
    
                  Person p3 = new Person();
                  p3.setAge(20);
                  p3.setName("John");
    
                  add(p1);
                  add(p2);
                  add(p3);
              }
          };
          //取出全部age字段爲一個List
          List<Integer> sumAge = ps.stream().map(Person::getAge).collect(Collectors.toList());
          System.out.println(sumAge);
          //取出age最大的那
          Integer maxAge =sumAge.stream().max(Integer::compare).get();
          System.out.println(maxAge);
      }
    
      class Person{
    
      private String name;
      private Integer age;
    
      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;
      }
     }

    五. Stream(流)的效率

  • 模擬非耗時簡單業務邏輯
class Person{
    private String name;
    private int age;
    private Date joinDate;
    private String label;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public Date getJoinDate() {
        return joinDate;
    }

    public void setJoinDate(Date joinDate) {
        this.joinDate = joinDate;
    }

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }
public class DataLoopTest {
    private static final Logger LOG= LoggerFactory.getLogger(DataLoopTest.class);

    private static final List<Person> persons = new ArrayList<>();
    static {
        for(int i=0;i<=1000000;i++){
            Person p = new Person();
            p.setAge(i);
            p.setName("zhangSan");
            p.setJoinDate(new Date());
            persons.add(p);
        }
    }

    /**
     * for 循環耗時 ===> 1.988
     * for 循環耗時 ===> 2.198
     * for 循環耗時 ===> 1.978
     *
     */
    @Test
    public void forTest(){
        Instant date_start = Instant.now();
        int personSize = persons.size();
        for(int i=0;i<personSize;i++){
            persons.get(i).setLabel(persons.get(i).getName().concat("-"+persons.get(i).getAge()).concat("-"+persons.get(i).getJoinDate().getTime()));
        }
        Instant date_end = Instant.now();
        LOG.info("for 循環耗時 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0);
    }

    /**
     *  forEach 循環耗時 ===> 1.607
     *  forEach 循環耗時 ===> 2.242
     *  forEach 循環耗時 ===> 1.875
     */
    @Test
    public void forEach(){
        Instant date_start = Instant.now();
        for(Person p:persons){
            p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime()));
        }
        Instant date_end = Instant.now();
        LOG.info("forEach 循環耗時 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0);
    }

    /**
     *  streamForeach 循環耗時 ===> 1.972
     *  streamForeach 循環耗時 ===> 1.969
     *  streamForeach 循環耗時 ===> 2.125
     */
    @Test
    public void streamForeach(){
        Instant date_start = Instant.now();
        persons.stream().forEach(p->p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime())));
        Instant date_end = Instant.now();
        LOG.info("streamForeach 循環耗時 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0);
    }

    /**
     *  parallelStreamForeach 循環耗時 ===> 1.897
     *  parallelStreamForeach 循環耗時 ===> 1.942
     *  parallelStreamForeach 循環耗時 ===> 1.642
     */
    @Test
    public void parallelStreamForeach(){
        Instant date_start = Instant.now();
        persons.parallelStream().forEach(p->p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime())));
        Instant date_end = Instant.now();
        LOG.info("parallelStreamForeach 循環耗時 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0);
    }

}
  • 模擬耗時簡單業務邏輯
public class DataLoopBlockTest {
    private static final Logger LOG= LoggerFactory.getLogger(DataLoopTest.class);

    private static final List<Person> persons = new ArrayList<>();
    static {
        for(int i=0;i<=100000;i++){
            Person p = new Person();
            p.setAge(i);
            p.setName("zhangSan");
            p.setJoinDate(new Date());
            persons.add(p);
        }
    }

    /**
     * for 循環耗時 ===> 101.385
     * for 循環耗時 ===> 102.161
     * for 循環耗時 ===> 101.472
     *
     */
    @Test
    public void forTest(){
        Instant date_start = Instant.now();
        int personSize = persons.size();
        for(int i=0;i<personSize;i++){
            try {
                Thread.sleep(1);
                persons.get(i).setLabel(persons.get(i).getName().concat("-"+persons.get(i).getAge()).concat("-"+persons.get(i).getJoinDate().getTime()));
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        Instant date_end = Instant.now();
        LOG.info("for 循環耗時 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0);
    }

    /**
     *  forEach 循環耗時 ===> 101.027
     *  forEach 循環耗時 ===> 102.488
     *  forEach 循環耗時 ===> 101.608
     */
    @Test
    public void forEach(){
        Instant date_start = Instant.now();
        for(Person p:persons){
            try {
                Thread.sleep(1);
                p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime()));
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        Instant date_end = Instant.now();
        LOG.info("forEach 循環耗時 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0);
    }

    /**
     *  streamForeach 循環耗時 ===> 103.246
     *  streamForeach 循環耗時 ===> 101.128
     *  streamForeach 循環耗時 ===> 102.615
     */
    @Test
    public void streamForeach(){
        Instant date_start = Instant.now();
        //persons.stream().forEach(p->p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime())));
        persons.stream().forEach(p->{
            try {
                Thread.sleep(1);
                p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime()));
            }catch (Exception e){
                e.printStackTrace();
            }
        });
        Instant date_end = Instant.now();
        LOG.info("streamForeach 循環耗時 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0);
    }

    /**
     *  parallelStreamForeach 循環耗時 ===> 51.391
     *  parallelStreamForeach 循環耗時 ===> 53.509
     *   parallelStreamForeach 循環耗時 ===> 50.831
     */
    @Test
    public void parallelStreamForeach(){
        Instant date_start = Instant.now();
        //persons.parallelStream().forEach(p->p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime())));
        persons.parallelStream().forEach(p->{
            try {
                Thread.sleep(1);
                p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime()));
            }catch (Exception e){
                e.printStackTrace();
            }
        });
        Instant date_end = Instant.now();
        LOG.info("parallelStreamForeach 循環耗時 ===> {}", Duration.between(date_start,date_end).toMillis()/1000.0);
        //LOG.info("\r\n===> {}",JSON.toJSONString(persons.get(10000)));
    }
}
能夠看到在<s>百萬數據</s>下作簡單數據循環處理,對於普通for(for\foreach)循環或stream(並行、非並行)下,幾者的效率差別並不明顯,
   注意: 在百萬數據下,普通for、foreach循環處理可能比stream的方式快許多,對於這點效率的損耗,其實lambda表達式對代碼的簡化更大!
   另外,在並行流的循環下速度提高了一倍之多,當單個循環耗時較多時,會拉大與前幾者的循環效率
    (以上測試僅對於循環而言,其餘類型業務處理,好比排序、求和、最大值等未作測試,我的猜想與以上測試結果類似)

六. Stream(流)注意項

  • 並行stream不是線程安全的,當對循壞外部統一對象進行讀寫時候會形成意想不到的錯誤,這須要留意
  • 因stream老是惰性的,原對象是不能夠被修改的,在集合處理完成後須要將處理結果放入一個新的集合容器內
  • 普通循環與stream(非並行)循環,在處理處理數據量比較大的時候效率是一致的,推薦使用stream的形式
  • 對於List刪除操做,目前只提供了removeIf方法來實現,並不能使用並行方式
  • 對於lambda表達式的寫法
  • 當表達式內只有一個返回boolean類型的語句時候語句是能夠簡寫的,例如:
persons.parallelStream().forEach(p->p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime())));
  • 當表達式內會有一些複雜處理邏輯時須要加上大括號,這與初始化List參數方式大體一致
persons.parallelStream().forEach(p->{
            try {
                Thread.sleep(1);
                p.setLabel(p.getName().concat("-"+p.getAge()).concat("-"+p.getJoinDate().getTime()));
            }catch (Exception e){
                e.printStackTrace();
            }
        });

七. stream&Lambda表達式經常使用api方法

  • 流到流之間的轉換類
    • filter(過濾)
    • map(映射轉換)
    • mapTo[Int|Long|Double] (到基本類型流的轉換)
    • flatMap(流展開合併)
    • flatMapTo[Int|Long|Double]
    • sorted(排序)
    • distinct(不重複值)
    • peek(執行某種操做,流不變,可用於調試)
    • limit(限制到指定元素數量)
    • skip(跳過若干元素)
  • 流到終值的轉換類
    • toArray(轉爲數組)
    • reduce(推導結果)
    • collect(聚合結果)
    • min(最小值)
    • max(最大值)
    • count (元素個數)
    • anyMatch (任一匹配)
    • allMatch(全部都匹配)
    • noneMatch(一個都不匹配)
    • findFirst(選擇首元素)
    • findAny(任選一元素)
  • 直接遍歷類
    • forEach (不保證順序遍歷,好比並行流)
    • forEachOrdered(順序遍歷)
  • 構造流類
    • empty (構造空流)
    • of (單個元素的流及多元素順序流)
    • iterate (無限長度的有序順序流)
    • generate (將數據提供器轉換成無限非有序的順序流)
    • concat (流的鏈接)
    • Builder (用於構造流的Builder對象)
相關文章
相關標籤/搜索