Java流及流操作示例
流
此處的流(Stream)與io中的輸入流(InputStream)與輸出流(OutputStream)是不同的概念,與實時處理資料的流也是不同的概念,但它們也有相似之處。
Stream是對集合類的增強,它將List、Map等集合作為資料來源,序列或並行地操作集合類,這些操作包括遍歷(foreach)、過濾(filter)、排序(sort)、匹配(match)、對映(map)、計數(count)等,上述操作分為兩種型別:
1、中間操作:該操作將會計算資料,返回計算後的流
2、終端操作:該操作會關閉流,返回最終資料
為了進行運算,Stream操作被放入到流管道中,流管道由資料來源(可以是陣列、集合、I/O通道等)、轉換流的中間操作(比如filter)、返回資料結果的最終操作組成(比如count和foreach)。
流管道示意圖:
Java中流特性:
-
不是資料結構
-
並不儲存資料,所有的操作從源抓取
-
不會影響源資料
-
不支援索引訪問(但可以使用IntStream曲線救國)
-
惰性
-
只有最終操作被啟動時,才會計算源資料
-
只有當需要的時候才使用源元素(提供源資料的元素)
-
-
並行
BaseStream
所有的流都繼承此介面
java // 繼承自AutoCloseable,說明流都是自動關閉的 public interface BaseStream<T, S extends BaseStream<T, S>> extends AutoCloseable { // 下面是所有流都擁有的方法 // 返回當前流元素的迭代器(終端操作) Iterator<T> iterator(); // 返回當前流元素的可切割迭代器(終端操作) Spliterator<T> spliterator(); // 是否並行流 boolean isParallel(); // 返回當前流的序列流,若當前流已經是序列或流已經被修改為序列,返回自己 S sequential(); // 返回當前流的並行流,若當前流已經是並行或流已經被修改為並行,返回自己 S parallel(); // 返回當前流的亂序流,若當前流已經是亂序或流已經被修改為亂序,返回自己 S unordered(); // 返回添加了額外的流關閉處理器的流,當close()方法執行的時候呼叫。 // 所有的流關閉處理器都會被執行,即使某處理器發生異常, S onClose(Runnable closeHandler); void close(); }
Java基於BaseStream提供了眾多資料流的封裝,其中基於原始資料型別的資料流封裝較為常用,比如:IntStream、DoubleStream、LongStream、
Stream
繼承自BaseStream
Stream API如下圖:
生成流的幾種方式
1、陣列方式
java
// 1.
Stream aryStream = Stream.of(ary);
// 2.
Arrays.stream(ary);
2、集合方式
java // 序列 List<String> list = new ArrayList<>(); list.add("1"); list.add("1"); list.add("1"); Stream stream = list.stream(); // 並行 List<String> list2 = new ArrayList<>(); list2.add("1"); list2.add("1"); list2.add("1"); Stream stream = list.parallelStream();
3、靜態工廠
java
// 這種方式可以解決下標問題
int[] ary = {1,2,3,4,5,6,7,8,9};
IntStream.range(0,ary.length).forEach(System.out::println);// 不包含10
IntStream.rangeClosed(0,ary.length).forEach(System.out::println);// 包含10
4、I/O
java
BufferedReader bufferedReader = new BufferedReader(new FileReader("project.properties"));
Stream<String> stream = bufferedReader.lines();
5、其他
java
// 1.
Random random = new Random();
IntStream stream1 = random.ints();
// 2.
Path path = Paths.get("D:\\記事本.txt");
Stream<String> lines = Files.lines(path, StandardCharsets.UTF_8);
常用操作示例
下文示例中使用到的Widget實體及集合
java
class Widget{
private String color;
private int weight;
// ...省略g&t
}
private Widget w1 = new Widget("RED",20);
private Widget w2 = new Widget("GREEN",20);
private Widget w3 = new Widget("GRAY",20);
private List<Widget> widgetList = new ArrayList<>();
{
widgetList.add(w1);
widgetList.add(w2);
widgetList.add(w3);
}
filter
java
@Test
public void stream(){
// 統計紅色小元件個數
System.out.println(widgetList.stream().filter(w -> w.getColor().equals("RED")).count());
// 統計紅色小元件總重
System.out.println(
widgetList.stream().filter(w -> w.getColor().equals("RED"))
// w -> w.getWeight()
// method reference 使用 Widget::getWeight 替代 w -> w.getWeight
// 方法引用可以用來替換λ表示式
.mapToInt(Widget::getWeight)
.sum()
);
}
filter的入參是謂詞 Predicate ,如果一個操作使用頻繁,我們可以先寫好一個 Predicate ,之後使用它就可以了
java
/*謂詞,filter方法接收的程式設計式函式(functional interface)*/
private static Predicate<Widget> isRedWidget(){
return widget -> widget.getColor().equals("RED");
}
/*filter forEach forEachRemaining*/
@Test
public void stream(){
// predicate
List<Widget> newWidgets = widgetList.stream().filter(isRedWidget()).collect(Collectors.toList());
newWidgets.forEach(System.out::println);
newWidgets.iterator().forEachRemaining(System.out::println);
}
// 輸出
[email protected]
[email protected]
map
java
@Test
public void map(){
String[] ary = {"Hello","world","John sena"};
// peek不是最終操作,所以下面這條語句不會輸出任何東西
// 這就驗證了前面提到的Streams are lazy
Arrays.stream(ary).map(String::toUpperCase).peek(word -> System.out.println("Mapped value:"+word));
Arrays.stream(ary).map(String::toUpperCase).peek(word -> System.out.println("Mapped value:"+word)).count();
}
// 輸出
Mapped value:HELLO
Mapped value:WORLD
Mapped value:JOHN SENA
collect
java
@Test
public void stream(){
List<Integer> list = Stream.of(1, 2, 3, 45, 6, 7, 8, 5, 435).collect(Collectors.toList());
System.out.println(list.toString());
String string = Stream.of("1","2").collect(Collectors.joining(","));
System.out.println(string);
Map widgetMap = widgetList.stream().collect(Collectors.groupingBy(Widget::getColor));
// 收集行為可以級聯
Map widgetMap2 = widgetList.stream().collect(Collectors.groupingBy(Widget::getColor,Collectors.groupingBy(Widget::getWeight)));
System.out.println(widgetMap.toString());
}
// 輸出
[1, 2, 3, 45, 6, 7, 8, 5, 435]
1,2
{RED=[[email protected]], GRAY=[[email protected]], GREEN=[[email protected]]}
reduce
java
/*Instant LongStream.rangeClosed*/
@Test
public void stream(){
// instant類用於獲取時間線上的時間點
Instant start = Instant.now();
// 計算範圍內的和
long r = LongStream.rangeClosed(0, 100000)
.parallel()// 並行流
// .sequential() 序列流
.reduce(0, Long::sum);
Instant end = Instant.now();
// 迭代器 forEach和forEachRemaining 的區別:
// forEach可以多次呼叫,對元素多次處理
// forEachRemaining對所有元素只處理一次
// 所以在第二次呼叫同一個迭代器的forEachRemaining方法時無作為
Iterator iterator = newWidgets.iterator();
iterator.forEachRemaining(System.out::println);
iterator.forEachRemaining(System.out::println);
}
// 輸出
5000050000
本次執行耗時:62
sort
java
/*sorted*/
@Test
public void stream(){
// 排序
Integer i[] = {1,2,3,5,6,7,9,45,9,324,22};
List<Integer> integers = Arrays.asList(i);
integers.stream().sorted().forEach(System.out::println);
}
// 輸出
1
2
3
5
6
7
9
9
22
45
324
distinct
java
/*distinct*/
@Test
public void stream(){
// 去重
Integer i[] = {1,2,2,2,27,2,2,9,324,22};
List<Integer> integers = Arrays.asList(i);
integers.stream().distinct().forEach(System.out::println);
}
// 輸出
1
2
27
9
324
22
下標
java
@Test
/*IntStream.range/rangeClosed*/
public void stream(){
int[] ary = {1,2,3,4,5,6,7,8,9};
// Stream aryStream = Stream.of(ary);
// Arrays.stream(ary);
//IntStream 可以用來當作遍歷的下標
IntStream.range(0,ary.length).forEach(System.out::println);// 不包含10
IntStream.rangeClosed(0,ary.length).forEach(System.out::println);// 包含10
IntStream.range(0,ary.length).forEach( i -> System.out.println(i*666));
}
flatMap
java
class Order{
private String orderNo;
private String orderName;
private List<OrderLineItem> lineItems;
//...省略g&t
}
class OrderLineItem{
private String lineNo;
private String lineName;
//...省略g&t
}
@Test
/*produce a new stream*/
public void stream(){
List<Order> orders = new ArrayList<>();
List<OrderLineItem> lineItems = new ArrayList<>();
lineItems.add(new OrderLineItem("1", "one"));
lineItems.add(new OrderLineItem("2", "two"));
lineItems.add(new OrderLineItem("1", "three"));
lineItems.add(new OrderLineItem("1", "four"));
lineItems.add(new OrderLineItem("1", "five"));
orders.add(new Order("1","one",lineItems));
orders.add(new Order("2", "two", lineItems));
orders.add(new Order("3", "three", lineItems));
// orders的流中將所有orders的lineItems組裝起來返回一個新的流,
// 這個流中有所有order的所有orderLineItem(在當前測試方法中有15個,3*5)
List data = orders.stream().flatMap(order -> order.getLineItems().stream()).collect(Collectors.toList());
System.out.println(data.size());
}
// 輸出
15
peek
java
/*peek*/
@Test
public void stream(){
// 就像名字一樣,窺視 233
List<String> list = Stream.of("one","two","three","four","five")
.filter(e -> e.length() > 3)
.peek(e -> System.out.println("Filtered value:"+e))
.map(String::toUpperCase)
.peek(e -> System.out.println("Maped value:"+e))
.collect(Collectors.toList());
}
// 輸出
Filtered value:three
Maped value:THREE
Filtered value:four
Maped value:FOUR
Filtered value:five
Maped value:FIVE
anyMatch/allMatch/noneMatch
java
@Test
public void match() {
// 匹配
boolean match = widgetList.stream().anyMatch(isRedWidget());
boolean match2 = widgetList.stream().allMatch(isRedWidget());
boolean match3 = widgetList.stream().noneMatch(isRedWidget());
System.out.println(match);
System.out.println(match2);
System.out.println(match3);
}
// 輸出
true
false
false
limit、skip
java
/*limit skip*/
@Test
public void stream(){
// 兩個作用類似,一個限制n個元素,一個跳過n個元素
List<Integer> list = Stream.of(1, 2, 3, 45, 6, 7, 8, 5, 435).limit(5).collect(Collectors.toList());
System.out.println(list.toString());
List<Integer> list2 = Stream.of(1, 2, 3, 45, 6, 7, 8, 5, 435).skip(5).collect(Collectors.toList());
System.out.println(list.toString());
}
// 輸出
[1, 2, 3, 45, 6]
[1, 2, 3, 45, 6]
min、max
java
@Test
public void minAndMax() {
Comparator comparator = (o1, o2) -> (int) o1 > (int) o2 ? 1 : -1;
Optional optional = Stream.of(1, 2, 3, 45, 6, 7, 8, 5, 435).min(comparator);
System.out.println(optional.get());
Optional optional2 = Stream.of(1, 2, 3, 45, 6, 7, 8, 5, 435).max(comparator);
System.out.println(optional2.get());
}
// 輸出
1
435
findFirst/findAny
java
@Test
public void find() {
// findFirst返回流中第一個元素
// findAny返回流中任意元素
Optional optional = widgetList.stream().findFirst();
Optional optional2 = widgetList.stream().findAny();
Widget widget = (Widget) optional.get();
Widget widget2 = (Widget) optional2.get();
System.out.println(widget.getColor());
System.out.println(widget2.getColor());
}
toArray
java
/*toArray*/
@Test
public void steam(){
Widget[] widgets = widgetList.stream().filter(widget -> widget.getColor().equals("RED")).toArray(Widget[]::new);
}