相對於Java8
以前的Java的相關操做簡直是天差地別,Java8 的流式操做的出現,也很大程度上改變了開發者對於Java的繁瑣的操做的印象,今後,Java也走向了函數式編程的道路!java
既然須要聊聊流的操做,那麼,首先仍是先看看怎麼建立流。編程
建立流的方法有三種,分別是:Stream.of()、Stream.iterate()、Stream.generate(),而後,分別看一下這三個方法的聲明。微信
static <T> Stream<T> of(T... values)
static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)
static <T> Stream<T> generate(Supplier<T> s)複製代碼
Stream.of()
:參數很簡單,就是一系列的泛型參數。Stream.iterate()
:第一個參數是一個初始值,第二個參數是一個操做。Stream.generate()
:參數就是一個Supplier的供給型的參數。app
/*
* @Author 歐陽思海
* @Description 建立流
* @Date 11:05 2019/8/26
* @Param []
* @return void
**/
@Test
public void testCreateStream() {
//利用Stream.of方法建立流
Stream<String> stream = Stream.of("hello", "world", "Java8");
stream.forEach(System.out::println);
System.out.println("##################");
//利用Stream.iterate方法建立流
Stream.iterate(10, n -> n + 1)
.limit(5)
.collect(Collectors.toList())
.forEach(System.out::println);
System.out.println("##################");
//利用Stream.generate方法建立流
Stream.generate(Math::random)
.limit(5)
.forEach(System.out::println);
System.out.println("##################");
//從現有的集合中建立流
List<String> strings = Arrays.asList("hello", "world", "Java8");
String string = strings.stream().collect(Collectors.joining(","));
System.out.println(string);
}複製代碼
在上面的例子中,Stream.of()
方法的參數是幾個字符串,Stream.iterate()
方法的第一個參數是初始值 10,第二個參數是在10 的基礎上每次加 1 的操做,Stream.generate()
的參數是用 Random 方法產生隨機數。dom
流的建立有三種方法,分別是Stream.of()、Stream.iterate()、Stream.generate(),這幾個都是 Stream
類的靜態方法,因此,使用起來很是的方便。ide
在上一節中,咱們知道怎麼建立流了,接下來,咱們就看看對流能夠進行哪些操做,使用了 Stream
流以後,是否會比 Java8
以前方便不少呢?函數式編程
在處理對象流的時候,能夠利用 Collectors
類的靜態方法轉換爲集合,例如,將字符串流轉換爲 List
,這種方式是沒有問題的。函數
可是,若是遇到 double流想要轉換爲 List
DoubleStream.of(1.0, 2.0, 3.0)
.collect(Collectors.toList());//錯誤的寫法複製代碼
這種方式就是錯誤的,編譯是不能經過的。測試
別慌,對於這種問題,有 3 種比較好的解決方法。
利用 boxed
方法,能夠將 DoubleStream
轉換爲 Stream
,例如;
DoubleStream.of(1.0, 2.0, 3.0)
.boxed()
.collect(Collectors.toList());複製代碼
這樣就解決了上面的問題。
利用 mapToObj
方法也能夠實現上面的功能,另外,也提供了 mapToInt、mapToLong、mapToDouble
等方法將基本類型流轉換爲相關包裝類型。
DoubleStream.of(1.0, 2.0, 3.0)
.mapToObj(Double::valueOf)
.collect(Collectors.toList());複製代碼
通常狀況下,咱們利用 collect
方法的時候,都是用於將流的數據收集爲基本類型的集合,例如;
stream.collect(Collectors.toList())複製代碼
然而,collect
方法其實還有一種更加通常化的形式,以下;
<R> R collect(Supplier<R> supplier,
ObjIntConsumer<R> accumulator,
BiCnsumer<R,R> combiner)複製代碼
上面這種方法的第一個參數是一個供給器,至關於初始化一個容器,第二個參數是累加器,至關於給初始化的容器賦值,第三個參數是組合器,至關於將這些元素所有組合到一個容器。
下面,咱們經過一個簡單的例子來看看究竟是怎麼使用的!
List<Double> list = DoubleStream.of(1.0, 2.0, 3.0)
.collect(ArrayList<Double>::new, ArrayList::add, ArrayList::addAll);複製代碼
上面的例子咱們能夠看到,第一個參數:使用一個靜態方法初始化一個 List
容器,第二個參數:使用靜態方法 add
,添加元素,第三個參數:使用靜態方法 addAll
,用於聯合全部的元素。
從最後的返回值爲 List
,咱們也能夠看出,所有組合成一個初始化的 List
集合中了。
這一小節主要講解一下字符串與流之間的轉換,將 String 轉爲流有兩種方法,分別是 java.lang.CharSequence
接口定義的默認方法 chars
和 codePoints
,而將流轉爲字符串就是咱們前面已經講解到的方法 collect
。
/*
* @Author 歐陽思海
* @Description 字符串與流之間的轉換
* @Date 9:41 2019/9/2
* @Param []
* @return void
**/
@Test
public void testString2Stream() {
String s = "hello world Java8".codePoints()//轉換成流
.collect(StringBuffer::new,
StringBuffer::appendCodePoint,
StringBuffer::append)//將流轉換爲字符串
.toString();
String s1 = "hello world Java8".chars()//轉換成流
.collect(StringBuffer::new,
StringBuffer::appendCodePoint,
StringBuffer::append)//將流轉換爲字符串
.toString();
}
複製代碼
在上面的例子中,先用chars
和 codePoints
方法轉換爲流,而後都是利用 collect
方法再轉回字符串。
流的映射是什麼意思呢,咱們先將一個在 Java8 以前的例子,咱們經常須要將一個集合的對象的某一個字段取出來,而後再存到另一個集合中,這種場景咱們在 Java8 以前咱們會這樣實現。
/*
* @Author 歐陽思海
* @Description Java8以前的用法
* @Date 19:31 2019/9/2
* @Param []
* @return void
**/
@Test
public void testList() {
List<Person> list = new ArrayList<>();
List<Friend> friends = new ArrayList<>();
friends.add(new Friend("Java5"));
friends.add(new Friend("Java6"));
friends.add(new Friend("Java7"));
Person person = new Person();
person.setFriends(friends);
list.add(person);
List<String> strings = new ArrayList<>();
for(Person p : list){
strings.add(p.getName());
}
}複製代碼
是否是這樣很麻煩,這也就是之前你們一直所說的 Python 用一招,Java 須要用花招!
可是,Java8
卻改變了這種現實,咱們來看一看怎麼使用 map
和 flatMap
。
首先,咱們先看一下這倆個方法的聲明;
<R> Stream<R> map(Function<? super T,? extends R> mapper)
<R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)複製代碼
接下來,咱們用這兩個方法改寫上面的方式,先看看 map
方法;
/*
* @Author 歐陽思海
* @Description map、flatMap方法
* @Date 9:50 2019/9/2
* @Param []
* @return void
**/
@Test
public void testMapAndFlatMap() {
List<Person> list = new ArrayList<>();
List<Friend> friends = new ArrayList<>();
friends.add(new Friend("Java5"));
friends.add(new Friend("Java6"));
friends.add(new Friend("Java7"));
Person person = new Person();
person.setFriends(friends);
list.add(person);
//映射出名字
List<String> strings = list.stream().map(Person::getName).collect(Collectors.toList());
}複製代碼
經過使用 map
方法,參數給定 Person::getName
映射出 name
,而後再用 collect
收集到 List
中,就完成了上面的負責的操做,是否是很舒服。
可是,若是咱們用 map 方法想要映射出 friends
屬性,會遇到一個問題;
//映射出朋友
List<List<Friend>> collect = list.stream().map(Person::getFriends).collect(Collectors.toList());複製代碼
咱們發現,上面的返回值是 List
,這種形式集合裏面還包着集合,處理有點麻煩,可是,不是還有另外
flatMap
沒有使用嗎,這個方法正好可以解決這個問題。
List<Friend> collect1 = list.stream().flatMap(friend -> friend.getFriends().stream()).collect(Collectors.toList());複製代碼
發現,這個方法的返回值是 List
,正如咱們看到的,flatMap
的方法可以「展平」包裹的流,這就是 map
和 flatMap
的區別。
流的鏈接有兩種方式,若是是兩個流的鏈接,使用 Stream.concat
方法,若是是三個及三個以上的流的鏈接,就使用 Stream.flatMap
方法。
/**
* @return void
* @Author 歐陽思海
* @Description 流的鏈接
* @Date 10:13 2019/9/2
* @Param []
**/
@Test
public void testConcatStream() {
//兩個流的鏈接
Stream<String> first = Stream.of("sihai", "sihai2", "sihai3");
Stream<String> second = Stream.of("sihai4", "sihai5", "sihai6");
Stream<String> third = Stream.of("siha7", "sihai8", "sihai9");
Stream<String> concat = Stream.concat(first, second);
//多個流的鏈接
Stream<String> stringStream = Stream.of(first, second, third).flatMap(Function.identity());
}複製代碼
流的規約操做幾種類型,這裏都講一下。
基本類型流都有內置的規約操做。包括average、count、max、min、sum、summaryStatistics,前面的幾個方法相信不用說了,summaryStatistics
方法是前面的幾個方法的結合,下面咱們看看他們如何使用。
/**
* @return void
* @Author 歐陽思海
* @Description 內置規約操做
* @Date 22:04 2019/9/1
* @Param []
**/
@Test
public void testReduce1() {
String[] strings = {"hello", "sihai", "hello", "Java8"};
long count = Arrays.stream(strings)
.map(String::length)
.count();
System.out.println(count);
System.out.println("##################");
int sum = Arrays.stream(strings)
.mapToInt(String::length)
.sum();
System.out.println(sum);
System.out.println("##################");
OptionalDouble average = Arrays.stream(strings)
.mapToInt(String::length)
.average();
System.out.println(average);
System.out.println("##################");
OptionalInt max = Arrays.stream(strings)
.mapToInt(String::length)
.max();
System.out.println(max);
System.out.println("##################");
OptionalInt min = Arrays.stream(strings)
.mapToInt(String::length)
.min();
System.out.println(min);
DoubleSummaryStatistics statistics = DoubleStream.generate(Math::random)
.limit(1000)
.summaryStatistics();
System.out.println(statistics);
}複製代碼
就是這麼簡單!
基本的規約操做是利用前面講過的 reduce
方法實現的,IntStream
接口定義了三種 reduce
方法的重載形式,以下;
OptionalInt reduce(IntBinaryOperator op)
int reduce(int identity, IntBianryOperator op)
<U> U reduce(U identity,
BiFunction<U,? super T,U> accumulator,
BianryOperator<U> combiner)複製代碼
上面的 identity
參數就是初始化值的意思,IntBianryOperator
類型的參數就是操做,例如 lambda
表達式;BianryOperator combiner
是一個組合器,在前面有講過。
下面咱們經過一個例子來說解一下。
/**
* @return void
* @Author 歐陽思海
* @Description reduce規約操做
* @Date 22:20 2019/9/1
* @Param []
**/
@Test
public void testReduce2() {
int sum = IntStream.range(1, 20)
.reduce((x, y) -> x + y)
.orElse(0);
System.out.println(sum);
System.out.println("##################");
int sum2 = IntStream.range(1, 20)
.reduce(0, (x, y) -> x + 2 * y);
System.out.println(sum2);
System.out.println("##################");
int sum3 = IntStream.range(1, 20)
.reduce(0, Integer::sum);
System.out.println(sum3);
}複製代碼
例子中的第一個是1到20累加的操做,第二個以0爲初始值,而後2倍累加,第三個是以0爲初始值,累加。
流的數量統計有兩種方法,分別是 Stream.count()
方法和 Collectors.counting()
方法。
/**
* @return void
* @Author 歐陽思海
* @Description 統計測試
* @Date 23:29 2019/9/1
* @Param []
**/
@Test
public void testStatistics() {
//統計數量
String[] strings = {"hello", "sihai", "hello", "Java8"};
long count = Arrays.stream(strings)
.count();
System.out.println(count);
System.out.println("##################");
Long count2 = Arrays.stream(strings)
.collect(Collectors.counting());
System.out.println(count2);
}複製代碼
流的查找 Stream 接口提供了兩個方法 findFirst
和 findAny
。
findFirst
方法返回流中的第一個元素的 Optional
,而 findAny
方法返回流中的某個元素的 Optional
。
咱們來看一個例子。
String[] strings = {"hello", "sihai", "hello", "Java8"};
Optional<String> first = Arrays.stream(strings)
.findFirst();
System.out.println(first.get());
System.out.println("##################");
Optional<String> any = Arrays.stream(strings).findAny();
System.out.println(any.get());
System.out.println("##################");複製代碼
流的匹配 Stream 接口提供了三個方法,分別是 anyMatch
(任何一個元素匹配,返回 true)、allMatch
(全部元素匹配,返回 true)、noneMatch
(沒有一個元素匹配,返回 true)。
boolean b = Stream.of(1, 2, 3, 4, 5, 10)
.anyMatch(x -> x > 5);
System.out.println(b);
System.out.println("##################");
boolean b2 = Stream.of(1, 2, 3, 4, 5, 10)
.allMatch(x -> x > 5);
System.out.println(b2);
System.out.println("##################");
boolean b3 = Stream.of(1, 2, 3, 4, 5, 10)
.noneMatch(x -> x > 5);
System.out.println(b3);複製代碼
這篇文章主要講解了流的一些操做,包括下面幾個方面。
- 流的建立方法。
- 流的系列操做,包括裝箱流、字符串與流之間的轉換、流和映射 map 和 flatMap、流的鏈接。
- 流的規約操做
- 流的查找與匹配
文章有不當之處,歡迎指正,若是喜歡微信閱讀,你也能夠關注個人微信公衆號:
好好學java
,獲取優質學習資源。