Java8的新特性(三)
強大的Stream API
瞭解Stream
Java8中有兩個最為重要的改變。第一個是Lambda表示式;另外一個則是Stream API(java.util.stream.*)。
Stream是Java8中處理集合的關鍵抽象概念,它可以指定你希望對集合進行的操作,可以執行非常複雜的查詢、過濾和對映資料等操作。使用Stream API對集合資料進行操作,就類似於使用SQL執行資料庫查詢,也可以使用Stream API來並行執行操作。簡而言之,Stream API提供了一種高效且易於使用的處理資料的方式。
什麼是Stream
Stream操作的三個步驟:
建立流四種方式:
@Test
public void test1() {
//1.可以通過Collection系列集合提供的Stream()或parallelStream()
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
//2.通過Arrays中的靜態方法Stream()可以來獲取一個數組流
Employee[] emps = new Employee [10];
Stream<Employee> stream2 = Arrays.stream(emps);
//3.通過Stream類裡面的靜態方法of()來建立流
Stream<String> stream3 = Stream.of("aa", "bb", "cc");
//4.建立無限流
//4.1迭代
Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
//我們可以來個終止的操作來看一下效果
stream4.limit(10).forEach(System.out::println);
//4.2生成
Stream<Double> stream5 = Stream.generate(() -> (double) Math.random());
stream5.limit(5)
.forEach(System.out::println);
}
Stream_篩選與切片
篩選與切片
- filter——接收 Lambda , 從流中排除某些元素。
- limit——截斷流,使其元素不超過給定數量。
- skip(n) —— 跳過元素,返回一個扔掉了前 n 個元素的流。若流中元素不足 n 個,則返回一個空流。與 limit(n) 互補
- distinct——篩選,通過流所生成元素的 hashCode() 和 equals() 去除重複元素
我們需要注意的是:
- 多箇中間操作可以連線起來形成一個流水線,除非流水線上觸發了終止操作,否則中間操作不會執行任何的處理!而是在終止操作時一次性全部處理,稱為惰性求值。
- 內部迭代:迭代操作是由Stream API來進行操作
(一)filter:接收 Lambda , 從流中排除某些元素
上面的寫法就是一個內部的迭代,它是由Stream API來進行完成;
而我們的外部迭代可以這樣來寫:
@Test
public void test2() {
Iterator<Employee> it = employees.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
(二)limit——截斷流,使其元素不超過給定數量
limit有一個短路的特性:只要找到了符合我們的資料結果,就不再進行迭代的操作,可以提高執行的效率
程式碼如下:
@Test
public void test3() {
employees.stream()
.filter((e)->e.getSalary()>5000)
.limit(2)
.forEach(System.out::println);
}
執行結果如下圖:
(三)skip(n) —— 跳過元素,返回一個扔掉了前 n 個元素的流。若流中元素不足 n 個,則返回一個空流。與 limit(n) 互補
程式碼如下:
//中間操作
List<Employee> employees = Arrays.asList(
new Employee(102, "李四", 59, 6666.66),
new Employee(101, "張三", 18, 9999.99),
new Employee(103, "王五", 28, 3333.33),
new Employee(104, "趙六", 8, 7777.77),
new Employee(104, "趙六", 8, 7777.77),
new Employee(104, "趙六", 8, 7777.77),
new Employee(105, "田七", 38, 5555.55));
@Test
public void test4() {
employees.stream()
.filter((e) -> e.getSalary() > 5000)
.skip(2)
.forEach(System.out::println);
}
效果如下圖:
(四)distinct——篩選,通過流所生成元素的 hashCode() 和 equals() 去除重複元素
需要注意的地方:要是想要去重,所去重的物件必須要重寫hashCode() 和 equals() :
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return id == employee.id &&
age == employee.age &&
Double.compare(employee.salary, salary) == 0 &&
Objects.equals(name, employee.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name, age, salary);
}
如下圖:
Stream_對映
對映
- map——接收 Lambda , 將元素轉換成其他形式或提取資訊。接收一個函式作為引數,該函式會被應用到每個元素上,並將其對映成一個新的元素。
- flatMap——接收一個函式作為引數,將流中的每個值都換成另一個流,然後把所有流連線成一個流
map對映的初體驗:
@Test
public void test7() {
List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
list.stream()
.map((str)-> str.toUpperCase())
.forEach(System.out::println);
}
效果如下:
我們經常會遇到的一個需求:提取員工的名字
效果如下圖:
現在我們寫上一個方法:傳入一個字串,把它轉為字元陣列,並把它放入到集合當中:這個方法返回的是一個Stream流,這個時候,如果我們想要對其進行遍歷 ,那麼我們要進行兩次的Stream的遍歷,非常的麻煩:
//中間操作
List<Employee> employees = Arrays.asList(
new Employee(102, "李四", 59, 6666.66),
new Employee(101, "張三", 18, 9999.99),
new Employee(103, "王五", 28, 3333.33),
new Employee(104, "趙六", 8, 7777.77),
new Employee(104, "趙六", 8, 7777.77),
new Employee(104, "趙六", 8, 7777.77),
new Employee(105, "田七", 38, 5555.55)
);
//flatMap-接受一個新的函式作為引數,接流中的每一個值都作為另外一個流,然後把所有的流連線為一個流
@Test
public void test9() {
List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
Stream<Stream<Character>> stream = list.stream()
.map(TestStreamAPI2::filterCharacher);
stream.forEach((sm)->{
sm.forEach(System.out::println);
});
}
//這個就是傳入一個字串,轉為字元陣列,並存入到一個集合中
public static Stream<Character> filterCharacher(String str) {
List<Character> list = new ArrayList<>();
for (Character ch : str.toCharArray()) {
list.add(ch);
}
return list.stream();
}
這個時候,我們就是可以用到flatMap來進行操作:
這個時候,我們就是可以這樣來寫:
//中間操作
List<Employee> employees = Arrays.asList(
new Employee(102, "李四", 59, 6666.66),
new Employee(101, "張三", 18, 9999.99),
new Employee(103, "王五", 28, 3333.33),
new Employee(104, "趙六", 8, 7777.77),
new Employee(104, "趙六", 8, 7777.77),
new Employee(104, "趙六", 8, 7777.77),
new Employee(105, "田七", 38, 5555.55)
);
@Test
public void test10() {
List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
list.stream()
.flatMap(TestStreamAPI2::filterCharacher)
.forEach(System.out::println);
}
//這個就是傳入一個字串,轉為字元陣列,並存入到一個集合中
public static Stream<Character> filterCharacher(String str) {
List<Character> list = new ArrayList<>();
for (Character ch : str.toCharArray()) {
list.add(ch);
}
return list.stream();
}
map和flatMap就類似於集合當中的add()方法和addAll(),如果add(List list),那麼這個時候,是把list當作一個元素新增進去,而addAll(List list)是把這個list集合裡面的所有元素新增進去;
map就是相當於把一個一個的流新增的流中,而flatMap是把流中的一個一個的元素新增到流中;
Stream_排序
排序
- sorted()——自然排序(Comprable)
- sorted(Comparator com)——定製排序(Comparator)
@Test
public void test12() {
List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
list.stream()
.sorted()
.forEach(System.out::println);
/** 先按年齡排,如果年齡一樣,再按姓名排 */
employees.stream()
.sorted((x, y) -> {
if(x.getAge() == y.getAge()){
/**年齡一樣,就按照姓名排*/
return x.getName().compareTo(y.getName());
}else{
/** 年齡不一樣,這個時候,就按照年齡排*/
return Integer.compare(x.getAge(), y.getAge());
}
}).forEach(System.out::println);
}
排序結果如下:
Stream_查詢與匹配
終止操作
- allMatch——檢查是否匹配所有元素
- anyMatch——檢查是否至少匹配一個元素
- noneMatch——檢查是否沒有匹配的元素
- findFirst——返回第一個元素
- findAny——返回當前流中的任意元素
- count——返回流中元素的總個數
- max——返回流中最大值
- min——返回流中最小值
- allMatch——檢查是否匹配所有元素
List<Employee> employees = Arrays.asList(
new Employee(102, "李四", 59, 6666.66, Status.BUSY),
new Employee(101, "張三", 18, 9999.99, Status.FREE),
new Employee(103, "王五", 28, 3333.33, Status.VOCATION),
new Employee(104, "趙六", 8, 7777.77, Status.BUSY),
new Employee(104, "趙六", 8, 7777.77, Status.FREE),
new Employee(104, "趙六", 8, 7777.77, Status.FREE),
new Employee(105, "田七", 38, 5555.55, Status.BUSY)
);
@Test
public void test1() {
boolean b1 = employees.stream()
//allMatch-檢查是否匹配到所有的元素
.allMatch((e) -> e.getStatus().equals(Status.BUSY));
System.out.println(b1);//沒有匹配到所有的元素,這個時候,返回的就是一個false
}
- anyMatch——檢查是否至少匹配一個元素
List<Employee> employees = Arrays.asList(
new Employee(102, "李四", 59, 6666.66, Status.BUSY),
new Employee(101, "張三", 18, 9999.99, Status.FREE),
new Employee(103, "王五", 28, 3333.33, Status.VOCATION),
new Employee(104, "趙六", 8, 7777.77, Status.BUSY),
new Employee(104, "趙六", 8, 7777.77, Status.FREE),
new Employee(104, "趙六", 8, 7777.77, Status.FREE),
new Employee(105, "田七", 38, 5555.55, Status.BUSY)
);
@Test
public void test2() {
boolean b1 = employees.stream()
//anyMatch-檢查是否至少匹配一個元素
.anyMatch((e) -> e.getStatus().equals(Status.BUSY));
System.out.println(b1);//這個就是返回true的,檢查是否至少有一個匹配到元素
}
- noneMatch——檢查是否沒有匹配的元素
List<Employee> employees = Arrays.asList(
new Employee(102, "李四", 59, 6666.66, Status.BUSY),
new Employee(101, "張三", 18, 9999.99, Status.FREE),
new Employee(103, "王五", 28, 3333.33, Status.VOCATION),
new Employee(104, "趙六"