平時工做用python的機會比較多,習慣了python函數式編程的簡潔和優雅。切換到java後,對於數據處理的『冗長代碼』仍是有點不習慣的。有幸的是,Java8版本後,引入了Lambda表達式和流的新特性,當流和Lambda表達式結合起來一塊兒使用時,由於流申明式處理數據集合的特色,可讓代碼變得簡潔易讀。幸福感爆棚,有沒有!java
本文主要列舉一些stream的使用例子,並附上相應代碼。python
先準備測試用的數據,這裏簡單聲明瞭一個Person
類,有名稱和年齡兩個屬性,採用 lombok 註解方式節省了一些模板是的代碼,讓代碼更加簡潔。git
@Data @AllArgsConstructor @NoArgsConstructor private static class Person { private String name; private Integer age; } private List<Person> initPersonList() { return Lists.newArrayList(new Person("Tom", 18), new Person("Ben", 22), new Person("Jack", 16), new Person("Hope", 4), new Person("Jane", 19), new Person("Hope", 16)); }
數據集合過Predicate方法,留下返回true的數據集合spring
@Test public void filterTest() { List<Person> personList = initPersonList(); // 過濾出年齡大於8的數據 List<Person> result = personList.stream().filter(x -> x.getAge() > 18).collect(Collectors.toList()); log.info(JsonUtils.toJson(result)); // filter 鏈式調用實現 and result = personList.stream().filter(x -> x.getAge() > 18).filter(x -> x.getName().startsWith("J")).collect(Collectors.toList()); log.info(JsonUtils.toJson(result)); // 經過 Predicate 實現 or Predicate<Person> con1 = x -> x.getAge() > 18; Predicate<Person> con2 = x -> x.getName().startsWith("J"); result = personList.stream().filter(con1.or(con2)).collect(Collectors.toList()); log.info(JsonUtils.toJson(result)); }
以上是filter的例子,可使用鏈式調用實現『與』的邏輯。經過聲明Predicate
,並使用 or 實現『或』邏輯編程
R apply(T t)
,所以map是一對一映射數據集合通過map方法後生成的數據集合,數據個數保持不變,即一對一映射數據結構
@Test public void mapTest() { List<Person> personList = initPersonList(); List<String> result = personList.stream().map(Person::getName).collect(Collectors.toList()); log.info(JsonUtils.toJson(result)); Set<String> nameSet = personList.stream().filter(x -> x.getAge() < 20).map(Person::getName).collect(Collectors.toSet()); log.info(JsonUtils.toJson(nameSet)); }
map比較簡單,這裏不贅述了,直接看代碼app
從圖中也能夠看到一對多映射,例如紅色圓圈通過flapmap後變成了2個(一個菱形、一個方形)ide
@Test public void flatMapTest() { List<Person> personList = initPersonList(); List<String> result = personList.stream().flatMap(x -> Arrays.stream(x.getName().split("n"))).collect(Collectors.toList()); log.info(JsonUtils.toJson(result)); }
以上代碼打印:["Tom","Be","Jack","Hope","Ja","e","Hope"]
,對每一個人的姓名用字母n作了切分函數式編程
R apply(T t, U u)
,即把兩個值reduce爲一個值下面是1+2+3+4+5的例子,能夠用reduce來解決函數
@Test public void reduceTest() { Integer sum = Stream.of(1, 2, 3, 4, 5).reduce(0, Integer::sum); Assert.assertEquals(15, sum.intValue()); sum = Stream.of(1, 2, 3, 4, 5).reduce(10, Integer::sum); Assert.assertEquals(25, sum.intValue()); String result = Stream.of("1", "2", "3") .reduce("0", (x, y) -> (x + "," + y)); log.info(result); }
對應示例圖的代碼實現,有數字求和的例子和字符串拼接的例子
collect在流中生成列表,map,等經常使用的數據結構。經常使用的有toList(), toSet(), toMap()
下面代碼列舉了幾個經常使用的場景
@Test public void collectTest() { List<Person> personList = initPersonList(); // 以name爲key, 創建name-person的映射,若是key重複,後者覆蓋前者 Map<String, Person> result = personList.stream().collect(Collectors.toMap(Person::getName, x -> x, (x, y) -> y)); log.info(JsonUtils.toJson(result)); // 以name爲key, 創建name-person_list的映射,即一對多 Map<String, List<Person>> name2Persons = personList.stream().collect(Collectors.groupingBy(Person::getName)); log.info(JsonUtils.toJson(name2Persons)); String name = personList.stream().map(Person::getName).collect(Collectors.joining(",", "{", "}")); Assert.assertEquals("{Tom,Ben,Jack,Hope,Jane,Hope}", name); // partitioningBy will always return a map with two entries, one for where the predicate is true and one for where it is false. It is possible that both entries will have empty lists, but they will exist. List<Integer> integerList = Arrays.asList(3, 4, 5, 6, 7); Map<Boolean, List<Integer>> result1 = integerList.stream().collect(Collectors.partitioningBy(i -> i < 3)); log.info(JsonUtils.toJson(result1)); result1 = integerList.stream().collect(Collectors.groupingBy(i -> i < 3)); log.info(JsonUtils.toJson(result1)); }
Collectors.toMap
的第三個參數就是BiFunction
,和reduce中的同樣,輸入兩個參數,返回一個參數。(x, y) -> y
就是(oldValue, newValue) -> oldValue
,若是不加這個方法,那麼當出現map的key重複,會直接拋異常Collectors.groupingBy
,上述例子就是用person的name作爲key,建議一對多映射關係groupingBy
和partitioningBy
的區別,前者是根據某個key進行分組,後者是分類,看他們的入參就明白了,groupingBy
的入參是Function
,partitioningBy
的入參是Predicate
,即返回的是true/false。因此partitioningBy
的key就是兩類,true和false(即便存在空列表,true 和 false 兩類仍是會存在)