lambda 流 peek java_JDK8 流与λ表达式
λ表達(dá)式
什么是λ表達(dá)式
λ表達(dá)式有三部分組成:參數(shù)列表,箭頭(->),以及一個(gè)表達(dá)式或者語句塊。
public int add(int x, int y) {
return x + y;
}
轉(zhuǎn)換為λ表達(dá)式
(int x, int y) -> x + y;
去除參數(shù)類型
(x, y) -> x + y;
無參 以及 只有一個(gè)參數(shù)
() -> { System.out.println("Hello Lambda!");
c -> { return c.size(); }
λ表達(dá)式的類型
λ表達(dá)式可以被當(dāng)做是一個(gè)Object(注意措辭)。λ表達(dá)式的類型,叫做“目標(biāo)類型(target type)”。λ表達(dá)式的目標(biāo)類型是“函數(shù)接口(functional interface)”,這是Java8新引入的概念。它的定義是:一個(gè)接口,如果只有一個(gè)顯式聲明的抽象方法,那么它就是一個(gè)函數(shù)接口。一般用@FunctionalInterface標(biāo)注出來(也可以不標(biāo))。舉例如下:
@FunctionalInterface
public interface Runnable{
void run();
}
public interface Callable{
V call() throws Exception;
}
public interface Comparator{
int compare(T o1, T o2); boolean equals(Object obj);
}
所以 可以定義
Runnable r1 = () -> {System.out.println("Hello Lambda!");};
思考:可否定義 Object o = () -> {System.out.println("Hello Lambda!");};
JDK常用的預(yù)定義接口函數(shù)
//入?yún)門,返回R
@FunctionalInterface
public interface Function {
R apply(T t);
}
//入?yún)門,無返回值
@FunctionalInterface
public interface Consumer {
void accept(T t);
}
//無入?yún)?#xff0c;返回T(通常配合構(gòu)造方法)
@FunctionalInterface
public interface Supplier {
T get();
}
//入?yún)門,返回值為boolean
@FunctionalInterface
public interface Predicate {
boolean test(T t);
}
//入?yún)門,U,返回值為R 類似還有BiConsumer等
@FunctionalInterface
public interface BiFunction {
R accept(T t, U u);
}
方法引用
方法引用讓開發(fā)者可以直接引用現(xiàn)存的方法、Java類的構(gòu)造方法或者實(shí)例對(duì)象。方法引用和Lambda表達(dá)式配合使用,使得java類的構(gòu)造方法看起來緊湊而簡(jiǎn)潔,沒有很多復(fù)雜的模板代碼。
public static class Car {
public static Car create( final Supplier supplier ) {
return supplier.get();
}
public static void collide( final Car car ) {
System.out.println( "Collided " + car.toString() );
}
public void follow( final Car another ) {
System.out.println( "Following the " + another.toString() );
}
public void repair() {
System.out.println( "Repaired " + this.toString() );
}
}
第一種方法引用的類型是構(gòu)造器引用,語法是Class::new,或者更一般的形式:Class::new。注意:這個(gè)構(gòu)造器沒有參數(shù)。
Car car = Car.create(Car::new);
List cars = Arrays.asList(car);
第二種方法引用的類型是靜態(tài)方法引用,語法是Class::static_method。注意:這個(gè)方法接受一個(gè)Car類型的參數(shù)。
cars.forEach(Car::collide);
第三種方法引用的類型是某個(gè)類的成員方法的引用,語法是Class::method,注意,這個(gè)方法沒有定義入?yún)?#xff1a;
cars.forEach(Car::repair);
第四種方法引用的類型是某個(gè)實(shí)例對(duì)象的成員方法的引用,語法是instance::method。注意:這個(gè)方法接受一個(gè)Car類型的參數(shù):
final Car police = Car.create(Car::new);
cars.forEach(police::follow);
流
什么是流
Stream 不是數(shù)據(jù)結(jié)構(gòu),不保存數(shù)據(jù),它是有關(guān)算法和計(jì)算的,就如同一個(gè)高級(jí)版本的迭代器(Iterator),單向,不可往復(fù),數(shù)據(jù)只能遍歷一次,遍歷過一次后即用盡了,就好比流水從面前流過,一去不復(fù)返。同時(shí)又與迭代器不同,迭代器只能串行操作,Stream可以并行化操作。
流的構(gòu)成
當(dāng)我們使用一個(gè)流的時(shí)候,通常包括三個(gè)基本步驟:
獲取一個(gè)數(shù)據(jù)源(source)→數(shù)據(jù)轉(zhuǎn)換→執(zhí)行操作獲取想要的結(jié)果,每次轉(zhuǎn)換原有 Stream 對(duì)象不改變,返回一個(gè)新的 Stream 對(duì)象(可以有多次轉(zhuǎn)換),這就允許對(duì)其操作可以像鏈條一樣排列,變成一個(gè)管道
,如下圖所示。
image
常用的構(gòu)建流的方式:
集合Collection
Collection.stream()
Collection.parallelStream()
數(shù)組
Stream.of(T[] tArray)
多個(gè)相同類型對(duì)象
Stream.of("chaimm","peter","john");
流的基本使用
常見操作
中間操作
無狀態(tài)
map (mapToInt, flatMap 等)、filter、peek
有狀態(tài)
distinct、sorted、limit、skip
終結(jié)操作
非短路
forEach、forEachOrdered、toArray、reduce、collect、min、 max、 count
短路操作
anyMatch、allMatch、noneMatch、findFirst、findAny
Stream中的操作可以分為兩大類:中間操作與終結(jié)操作
中間操作(Intermediate):一個(gè)流可以后面跟隨零個(gè)或多個(gè) intermediate 操作。其目的主要是打開流,做出某種程度的數(shù)據(jù)映射/過濾,然后返回一個(gè)新的流,交給下一個(gè)操作使用。這類操作都是惰性化的(lazy),就是說,僅僅調(diào)用到這類方法,并沒有真正開始流的遍歷。中間操作又可以分為無狀態(tài)(Stateless)操作與有狀態(tài)(Stateful)操作,前者是指元素的處理不受之前元素的影響;后者是指該操作只有拿到所有元素之后才能繼續(xù)下去。
終結(jié)操作(Terminal):一個(gè)流只能有一個(gè) terminal 操作,當(dāng)這個(gè)操作執(zhí)行后,流就被使用“光”了,無法再被操作。所以這必定是流的最后一個(gè)操作。Terminal 操作的執(zhí)行,才會(huì)真正開始流的遍歷,并且會(huì)生成一個(gè)結(jié)果。終結(jié)操作又可以分為短路與非短路操作,短路是指遇到某些符合條件的元素就可以得到最終結(jié)果,比如找到第一個(gè)滿足條件的元素。而非短路是指必須處理所有元素才能得到最終結(jié)果。
map/flatMap
對(duì)流中的每個(gè)元素執(zhí)行一個(gè)函數(shù),使得元素轉(zhuǎn)換成另一種類型輸出。
map 一對(duì)一 (入?yún)?Function)
List persons = new ArrayLisy<>();
List result = persons.stream().map(x -> x.getId())
.collect(Collectors.toList());
List result = persons.stream().map(x -> {
Student s = new Student();
s.setName(x.getName());
return s;
}).collect(Collectors.toList());
flatMap 一對(duì)多 (入?yún)?Function)
List> listAll = new ArrayList<>();
List result = listAll.stream().flatMap(x -> x.stream())
.collect(Collectors.toList());
List result = listAll.stream().flatMap(x -> x.stream())
.map(x -> {
Student s = new Student();
s.setName(x.getName());
return s;
}).collect(Collectors.toList());
filter
filter 對(duì)原始 Stream 進(jìn)行某項(xiàng)測(cè)試,通過測(cè)試的元素被留下來生成一個(gè)新 Stream。 (入?yún)?Predicate)
Integer[] nums = new Integer[]{1, 2, 3, 4, 5, 6};
Integer[] evens = Stream.of(nums).filter(n -> n%2 == 0)
.toArray(Integer[]::new);
List result = persons.stream().filter(x -> x.getAge() < 20)
.map(x -> {
Student s = new Student();
s.setName(x.getName());
return s;
}).collect(Collectors.toList());
peek
peek 方法我們可以拿到元素,然后做一些其他事情。(入?yún)?Consumer)
List result = persons.stream().map(x -> x.getId())
.peek(x -> System.out.println(x))
.collect(Collectors.toList());
Map map = new HashMap<>();
List result = persons.stream()
.filter(x -> x.getAge() < 20 )
.peek(x -> map.put(x.getId(), x) )
.map(x -> {
Student s = new Student();
s.setName(x.getName());
return s;
}).collect(Collectors.toList());
limit/skip
limit 返回 Stream 的前面 n 個(gè)元素,skip 則是扔掉前 n 個(gè)元素
List result = persons.stream().map(x -> x.getId())
.limit(10).skip(3).collect(Collectors.toList());
sorted
對(duì)元素進(jìn)行排序 (入?yún)?Comparator)
List personList2 = persons.stream()
.sorted((p1, p2) -> p1.getName().compareTo(p2.getName()))
.collect(Collectors.toList());
forEach
對(duì)元素進(jìn)行遍歷消費(fèi) (入?yún)?Consumer)
persons.stream().sorted((p1, p2) -> p1.getName().compareTo(p2.getName()))
.forEach(peron -> {
System.out.println(persion.getName());
});
collect
對(duì)元素進(jìn)行收集
List personList2 = persons.stream()
.sorted((p1, p2) -> p1.getName().compareTo(p2.getName()))
.collect(Collectors.toList());
List personList2 = persons.stream()
.sorted((p1, p2) -> p1.getName().compareTo(p2.getName()))
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
Collectors.toMap
轉(zhuǎn)換為MAP
Map personIdNameMap = persons.stream()
.collect(Collectors.toMap(Person::getId, Person::getName);
Map personIdNameMap = persons.stream()
.collect(Collectors.toMap(Person::getId, Person::getName, (v1,v2)->v1);
Collectors.groupingBy
分組
Map> personAgeMap = persons.stream()
.collect(Collectors.groupingBy(Person::getAge));
Map> personAgeMap = persons.stream()
.collect(Collectors.groupingBy(Person::getAge,
Collectors.toMap(Person::getId, Function.identity())));
Collectors.collectingAndThen
收集然后處理
Map personAgeMap = persons.stream()
.collect(Collectors.groupingBy(Person::getAge,
Collectors.collectingAndThen(
Collectors.toList(),
list->list.size()
)));
接口方法
接口default方法
默認(rèn)方法使得開發(fā)者可以在不破壞兼容性的前提下,往現(xiàn)存接口中添加新的方法,即不強(qiáng)制那些實(shí)現(xiàn)了該接口的類也同時(shí)實(shí)現(xiàn)這個(gè)新加的方法。
默認(rèn)方法和抽象方法之間的區(qū)別在于抽象方法需要實(shí)現(xiàn),而默認(rèn)方法不需要。接口提供的默認(rèn)方法會(huì)被接口的實(shí)現(xiàn)類繼承或者覆寫
private interface HelloService {
default String sayHello() {
return "hello";
}
}
private static class HelloImpl implements HelloService {
}
private static class HelloWorldImpl implements HelloService {
@Override
public String sayHello() {
return "hello world";
}
}
思考:為啥要加入default方法?
接口static方法
private interface HelloService {
static boolean testHello(String s) {
return Objects.equals(s,"hello");
}
}
Stream流水線解決方案
Stage(Pipeline)
java8用Stage來記錄Stream的中間操作,很多Stream操作會(huì)需要一個(gè)回調(diào)函數(shù)(Lambda表達(dá)式),因此一個(gè)完整的操作是構(gòu)成的三元組。Stream中使用Stage的概念來描述一個(gè)完整的操作,并用某種實(shí)例化后的Pipeline來代表Stage,然后將具有先后順序的各個(gè)Stage連到一起,就構(gòu)成了整個(gè)流水線。
image
Sink
有了操作,我們需要將所有操作疊加起來,讓流水線起到應(yīng)有的作用,java用Sink來協(xié)調(diào)相鄰Stage之間的調(diào)用關(guān)系。每個(gè)Stage必須實(shí)現(xiàn)opWrapSink方法。Sink接口的主要方法如下
方法名
作用
void begin(long size)
開始遍歷元素之前調(diào)用該方法,通知Sink做好準(zhǔn)備
void end()
所有元素遍歷完成之后調(diào)用,通知Sink沒有更多的元素了
boolean cancellationRequested()
是否可以結(jié)束操作,可以讓短路操作盡早結(jié)束
void accept(T t)
遍歷元素時(shí)調(diào)用,接受一個(gè)待處理元素,并對(duì)元素進(jìn)行處理
每個(gè)Stage都會(huì)將自己的操作封裝到一個(gè)Sink里,前一個(gè)Stage只需調(diào)用后一個(gè)方法即可,并不需要知道其內(nèi)部是如何處理的。當(dāng)然對(duì)于有狀態(tài)的操作,Sink的begin()和end()方法也是必須實(shí)現(xiàn)的。比如Stream.sorted()是一個(gè)有狀態(tài)的中間操作,其對(duì)應(yīng)的Sink.begin()方法可能創(chuàng)建一個(gè)乘放結(jié)果的容器,而accept()方法負(fù)責(zé)將元素添加到該容器,最后end()負(fù)責(zé)對(duì)容器進(jìn)行排序。對(duì)于短路操作,Sink.cancellationRequested()也是必須實(shí)現(xiàn)的,比如Stream.findFirst()是短路操作,只要找到一個(gè)元素,cancellationRequested()就應(yīng)該返回true,以便調(diào)用者盡快結(jié)束查找。Sink的四個(gè)接口方法常常相互協(xié)作,共同完成計(jì)算任務(wù)。實(shí)際上Stream API內(nèi)部實(shí)現(xiàn)的的本質(zhì),就是如何重載Sink的這四個(gè)接口方法。
map方法的主要實(shí)現(xiàn)
public final Stream map(Function super P_OUT, ? extends R> mapper) {
Objects.requireNonNull(mapper);
return new StatelessOp(this, StreamShape.REFERENCE,
StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) {
@Override
Sink opWrapSink(int flags, Sink sink) {
return new Sink.ChainedReference(sink) {
@Override
public void accept(P_OUT u) {
downstream.accept(mapper.apply(u));
}
};
}
};
}
static abstract class ChainedReference implements Sink {
protected final Sink super E_OUT> downstream;
public ChainedReference(Sink super E_OUT> downstream) {
this.downstream = Objects.requireNonNull(downstream);
}
@Override
public void begin(long size) {
downstream.begin(size);
}
@Override
public void end() {
downstream.end();
}
@Override
public boolean cancellationRequested() {
return downstream.cancellationRequested();
}
}
// Stream.sort()方法用到的Sink實(shí)現(xiàn)
class RefSortingSink extends AbstractRefSortingSink {
private ArrayList list;// 存放用于排序的元素
RefSortingSink(Sink super T> downstream, Comparator super T> comparator) {
super(downstream, comparator);
}
@Override
public void begin(long size) {
...
// 創(chuàng)建一個(gè)存放排序元素的列表
list = (size >= 0) ? new ArrayList((int) size) : new ArrayList();
}
@Override
public void end() {
list.sort(comparator);// 只有元素全部接收之后才能開始排序
downstream.begin(list.size());
if (!cancellationWasRequested) {// 下游Sink不包含短路操作
list.forEach(downstream::accept);// 2. 將處理結(jié)果傳遞給流水線下游的Sink
}
else {// 下游Sink包含短路操作
for (T t : list) {// 每次都調(diào)用cancellationRequested()詢問是否可以結(jié)束處理。
if (downstream.cancellationRequested()) break;
downstream.accept(t);// 2. 將處理結(jié)果傳遞給流水線下游的Sink
}
}
downstream.end();
list = null;
}
@Override
public void accept(T t) {
list.add(t);// 1. 使用當(dāng)前Sink包裝動(dòng)作處理t,只是簡(jiǎn)單的將元素添加到中間列表當(dāng)中
}
}
多個(gè)Sink疊加
多個(gè)Stage組成的鏈路如圖所示,那么什么時(shí)候出發(fā)執(zhí)行結(jié)束操作(Terminal Operation),一旦調(diào)用某個(gè)結(jié)束操作,就會(huì)觸發(fā)整個(gè)流水線的執(zhí)行。
final void copyInto(Sink wrappedSink, Spliterator spliterator){
...
if (!StreamOpFlag.SHORT_CIRCUIT.isKnown(getStreamAndOpFlags())) {
wrappedSink.begin(spliterator.getExactSizeIfKnown());// 通知開始遍歷
spliterator.forEachRemaining(wrappedSink);// 迭代
wrappedSink.end();// 通知遍歷結(jié)束
}
...
}
元空間
永久代的消除
從JDK1.7開始,貯存在永久代的一部分?jǐn)?shù)據(jù)已經(jīng)轉(zhuǎn)移到了Java Heap或者是Native Heap。符號(hào)引用(Symbols)轉(zhuǎn)移到了native heap;字面量(interned strings)轉(zhuǎn)移到了java heap;類的靜態(tài)變量(class statics)轉(zhuǎn)移到了java heap。但永久代仍然存在于JDK7,并沒有完全的移除。在JDK1.8版本中永久帶被徹底移除。永久代的參數(shù)-XX:PermSize和-XX:MaxPermSize也被移除。該參數(shù)在JDK1.8使用會(huì)有警告
元空間
JDK1.8將類信息存儲(chǔ)在元空間中,元空間并不在虛擬機(jī)中,而是使用本地內(nèi)存。因此,默認(rèn)情況下,元空間的大小僅受本地內(nèi)存限制,但可以通過以下參數(shù)來指定元空間的大小:
-XX:MetaspaceSize,初始空間大小,達(dá)到該值就會(huì)觸發(fā)垃圾收集進(jìn)行類型卸載,同時(shí)GC會(huì)對(duì)該值進(jìn)行調(diào)整:如果釋放了大量的空間,就適當(dāng)降低該值;如果釋放了很少的空間,那么在不超過MaxMetaspaceSize時(shí),適當(dāng)提高該值。
-XX:MaxMetaspaceSize,最大空間,默認(rèn)是沒有限制的(取決于內(nèi)存)。
總結(jié)
以上是生活随笔為你收集整理的lambda 流 peek java_JDK8 流与λ表达式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: centos eclipse java_
- 下一篇: html怎么弄艺术字体,如何为图片加上艺