บทนำสู่แลมบ์ดา
Lambda Expressions เป็นคุณสมบัติใหม่ที่สำคัญใน Java SE 8 การแสดงออกของแลมบ์ดาช่วยให้คุณสามารถแทนที่อินเทอร์เฟซที่ใช้งานได้โดยนิพจน์ นิพจน์แลมบ์ดานั้นเหมือนกับวิธีการซึ่งให้รายการพารามิเตอร์ปกติและร่างกาย (ร่างกายซึ่งสามารถเป็นนิพจน์หรือบล็อกรหัส) ที่ใช้พารามิเตอร์เหล่านี้
Lambda Expressions ยังปรับปรุงห้องสมุดคอลเลกชัน Java SE 8 เพิ่ม 2 แพ็คเกจที่ใช้งานแบทช์ในการรวบรวมข้อมูล: แพ็คเกจ java.util.function และแพ็คเกจ java.util.stream สตรีมเป็นเหมือนตัววนซ้ำ แต่มีคุณสมบัติเพิ่มเติมมากมายที่แนบมา โดยทั่วไปการแสดงออกของแลมบ์ดาและสตรีมเป็นการเปลี่ยนแปลงที่ยิ่งใหญ่ที่สุดเนื่องจากภาษา Java เพิ่มแผ่นดินไหวและคำอธิบายประกอบ
การแสดงออกของแลมบ์ดาเป็นวิธีการที่ไม่ระบุชื่อเป็นหลักและเลเยอร์พื้นฐานของพวกเขาจะถูกนำไปใช้ผ่านคำสั่ง invokedynamic เพื่อสร้างชั้นเรียนที่ไม่ระบุชื่อ มันมีไวยากรณ์ที่ง่ายกว่าและวิธีการเขียนช่วยให้คุณสามารถแทนที่อินเทอร์เฟซที่ใช้งานได้ด้วยนิพจน์ ในสายตาของบางคนแลมบ์ดาสามารถทำให้รหัสของคุณกระชับมากขึ้นและไม่ใช้เลย - มุมมองนี้ก็โอเค แต่สิ่งสำคัญคือแลมบ์ดานำการปิดไปสู่ชวา ด้วยการสนับสนุนของ Lamdba สำหรับคอลเลกชันแลมบ์ดาได้ปรับปรุงประสิทธิภาพอย่างมากเมื่อข้ามคอลเลกชันภายใต้เงื่อนไขโปรเซสเซอร์แบบหลายคอร์ นอกจากนี้เราสามารถประมวลผลคอลเลกชันในรูปแบบของสตรีมข้อมูล - ซึ่งน่าสนใจมาก
ไวยากรณ์แลมบ์ดา
ไวยากรณ์ของแลมบ์ดานั้นง่ายมากคล้ายกับโครงสร้างต่อไปนี้:
(พารามิเตอร์) -> นิพจน์
หรือ
(พารามิเตอร์) -> {คำสั่ง; -การแสดงออกของแลมบ์ดาประกอบด้วยสามส่วน:
1. Paramates: รายการของพารามิเตอร์อย่างเป็นทางการในวิธีการที่คล้ายกันพารามิเตอร์ที่นี่คือพารามิเตอร์ในส่วนต่อประสานการทำงาน ประเภทพารามิเตอร์ที่นี่สามารถประกาศได้อย่างชัดเจนหรือไม่ประกาศ แต่สรุปโดยปริยายโดย JVM นอกจากนี้เมื่อมีการอนุมานแบบเดียวเพียงวงเล็บสามารถละเว้นได้
2. ->: สามารถเข้าใจได้ว่า "กำลังใช้"
3. วิธีการ: มันอาจเป็นนิพจน์หรือบล็อกรหัสมันคือการใช้วิธีการในอินเทอร์เฟซที่ใช้งานได้ บล็อกโค้ดสามารถส่งคืนค่าหรือการกลับรายการไม่มีอะไร บล็อกรหัสที่นี่เทียบเท่ากับวิธีการของวิธีการ หากเป็นนิพจน์คุณสามารถส่งคืนค่าหรือไม่กลับอะไรเลย
ลองใช้ตัวอย่างต่อไปนี้เพื่อแสดง:
// ตัวอย่างที่ 1: ไม่จำเป็นต้องยอมรับพารามิเตอร์, ส่งคืนโดยตรง 10 ()-> 10 // ตัวอย่างที่ 2: ยอมรับพารามิเตอร์สองตัวของประเภท int และส่งคืนผลรวมของพารามิเตอร์ทั้งสองนี้ (int x, int y)-> x+y; // ตัวอย่าง 2: ยอมรับพารามิเตอร์สองตัวของ X และ Y ยอมรับสตริงและพิมพ์สตริงเพื่อควบคุมโดยไม่ย้อนกลับผลลัพธ์ (ชื่อสตริง)-> system.out.println (ชื่อ); // ตัวอย่างที่ 4: ยอมรับชื่อพารามิเตอร์ประเภทที่อนุมานได้และพิมพ์สตริงเป็นคอนโซลชื่อ-> system.out.println (ชื่อ); // ตัวอย่าง 5: ยอมรับพารามิเตอร์ประเภทสตริง เพศ)-> {system.out.println (ชื่อ); system.out.println (เพศ)} // ตัวอย่างที่ 6: ยอมรับพารามิเตอร์ x และส่งคืนพารามิเตอร์สองครั้ง x-> 2*xสถานที่ใช้แลมบ์ดา
ใน [อินเทอร์เฟซฟังก์ชัน] [1] เรารู้ว่าประเภทเป้าหมายของนิพจน์แลมบ์ดาเป็นอินเตอร์เฟสการทำงาน - แลมบ์ดาแต่ละตัวสามารถจับคู่ประเภทที่กำหนดผ่านอินเตอร์เฟสการทำงานที่เฉพาะเจาะจง ดังนั้นสามารถใช้นิพจน์แลมบ์ดาได้ทุกที่ที่ตรงกับประเภทเป้าหมาย นิพจน์แลมบ์ดาจะต้องมีประเภทพารามิเตอร์เดียวกันกับคำอธิบายฟังก์ชันนามธรรมของอินเทอร์เฟซการทำงานประเภทการส่งคืนของมันจะต้องเข้ากันได้กับประเภทการส่งคืนของฟังก์ชันนามธรรมและข้อยกเว้นที่สามารถโยนได้ถูก จำกัด อยู่ที่ช่วงคำอธิบายฟังก์ชั่น
ถัดไปดูตัวอย่างอินเทอร์เฟซฟังก์ชั่นที่กำหนดเอง:
@FunctionalInterface Interface Converter <f, t> {t แปลง (f จาก);}ก่อนอื่นให้ใช้อินเทอร์เฟซในแบบดั้งเดิม:
ตัวแปลง <สตริง, จำนวนเต็ม> converter = ตัวแปลงใหม่ <String, Integer> () {@Override Public Integer Convert (สตริงจาก) {return Integer.ValueOf (จาก); - ผลลัพธ์จำนวนเต็ม = converter.convert ("200"); System.out.println (ผลลัพธ์);เห็นได้ชัดว่าไม่มีปัญหากับเรื่องนี้ดังนั้นสิ่งต่อไปคือช่วงเวลาที่แลมบ์ดาเข้ามาในสนามโดยใช้แลมบ์ดาเพื่อใช้อินเทอร์เฟซตัวแปลง:
ตัวแปลง <สตริง, จำนวนเต็ม> converter = (param) -> integer.valueof (param); ผลลัพธ์จำนวนเต็ม = converter.convert ("101"); System.out.println (ผลลัพธ์);ผ่านตัวอย่างข้างต้นฉันคิดว่าคุณมีความเข้าใจง่าย ๆ เกี่ยวกับการใช้แลมบ์ดา ด้านล่างนี้เรากำลังใช้ Runnable ที่ใช้กันทั่วไปเพื่อแสดงให้เห็น:
ในอดีตเราอาจเขียนรหัสนี้:
เธรดใหม่ (ใหม่ runnable () {@Override โมฆะสาธารณะ run () {system.out.println ("สวัสดี lambda");}}) เริ่มต้น ();ในบางกรณีคลาสที่ไม่ระบุชื่อจำนวนมากสามารถทำให้โค้ดดูรกได้ ตอนนี้คุณสามารถใช้แลมบ์ดาเพื่อให้ง่าย:
เธรดใหม่ (() -> system.out.println ("สวัสดี lambda")). start ();วิธีการอ้างอิง
การอ้างอิงวิธีการเป็นวิธีที่ง่ายในการเขียนนิพจน์แลมบ์ดา วิธีการอ้างอิงคือการใช้งานของวิธีการของการแสดงออกของแลมบ์ดาและโครงสร้างไวยากรณ์คือ:
OBJECTREF :: MethodName
ด้านซ้ายสามารถเป็นชื่อคลาสหรือชื่ออินสแตนซ์กลางคือสัญลักษณ์อ้างอิงวิธีการ "::" และด้านขวาเป็นชื่อวิธีที่สอดคล้องกัน
การอ้างอิงวิธีการแบ่งออกเป็นสามประเภท:
1. การอ้างอิงวิธีการคงที่
ในบางกรณีเราอาจเขียนรหัสเช่นนี้:
การอ้างอิงคลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {converter <string, integer> converter = ตัวแปลงใหม่ <สตริง, จำนวนเต็ม> () {@Override Public Integer Convert (สตริงจาก) - Converter.Convert ("120"); } @functionalInterface Interface Converter <f, t> {t แปลง (f จาก); } int String2int แบบคงที่ (สตริงจาก) {return integer.valueof (จาก); -ในเวลานี้หากคุณใช้การอ้างอิงแบบคงที่รหัสจะกระชับมากขึ้น:
Converter <String, Integer> Converter = ReferenCetest :: String2int; Converter.Convert ("120");2. การอ้างอิงวิธีการอินสแตนซ์
เราอาจเขียนรหัสเช่นนี้:
การอ้างอิงคลาสสาธารณะ {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {converter <string, integer> converter = ตัวแปลงใหม่ <สตริง, จำนวนเต็ม> () {@Override Public Integer Convert (String จาก) {ส่งคืน helper ใหม่ (). String2int (จาก); - Converter.Convert ("120"); } @functionalInterface Interface Converter <f, t> {t แปลง (f จาก); } ตัวช่วยคลาสคงที่ {public int string2int (สตริงจาก) {return integer.valueof (จาก); -นอกจากนี้การใช้ตัวอย่างวิธีการอ้างอิงจะปรากฏขึ้นกระชับมากขึ้น:
Helper Helper = New Helper (); Converter <String, Integer> Converter = Helper :: String2int; Converter.Convert ("120");3. การอ้างอิงวิธีการสร้าง
ตอนนี้เรามาสาธิตการอ้างอิงถึงผู้สร้าง ก่อนอื่นเรากำหนดสัตว์ชั้นพ่อแม่:
คลาส Animal {ชื่อสตริงส่วนตัว; อายุ int ส่วนตัว; สัตว์สาธารณะ (ชื่อสตริงอายุ int) {this.name = ชื่อ; this.age = อายุ; } พฤติกรรมโมฆะสาธารณะ () {}} ต่อไปเรากำลังกำหนดสองคลาสย่อยของสัตว์: Dog、Bird
นกชั้นสาธารณะขยายสัตว์ {นกสาธารณะ (ชื่อสตริงอายุ int) {super (ชื่ออายุ); } @Override พฤติกรรมโมฆะสาธารณะ () {system.out.println ("บิน"); }} สุนัขชั้นเรียนขยายสัตว์ {สุนัขสาธารณะ (ชื่อสตริงอายุ int) {super (ชื่ออายุ); } @Override พฤติกรรมโมฆะสาธารณะ () {system.out.println ("เรียกใช้"); -จากนั้นเรากำหนดอินเทอร์เฟซโรงงาน:
อินเตอร์เฟสโรงงาน <t ขยายสัตว์> {t สร้าง (ชื่อสตริงอายุ int); -ต่อไปเราจะใช้วิธีการดั้งเดิมในการสร้างวัตถุของชั้นเรียนสุนัขและนก:
โรงงานโรงงาน = New Factory () {@Override Public Animal Create (ชื่อสตริงอายุ int) {ส่งคืนสุนัขใหม่ (ชื่ออายุ); - โรงงานสร้าง ("นามแฝง", 3); Factory = New Factory () {@Override Public Animal Create (ชื่อสตริงอายุ int) {ส่งคืนนกใหม่ (ชื่ออายุ); - Factory.create ("Smook", 2);ฉันเขียนรหัสมากกว่าสิบรหัสเพื่อสร้างวัตถุสองชิ้น ตอนนี้ลองใช้การอ้างอิงตัวสร้าง:
โรงงาน <imant> dogfactory = dog :: new; Animal Dog = dogfactory.create ("นามแฝง", 4); โรงงาน <เบิร์ด> นก = นก :: ใหม่; นกนก = Birdfactory.create ("Smook", 3); วิธีนี้รหัสจะดูสะอาดและเรียบร้อย เมื่อใช้ Dog::new เพื่อเจาะวัตถุให้เลือกฟังก์ชั่นการสร้างที่สอดคล้องกันโดยการลงนามในฟังก์ชั่นสร้าง Factory.create
โดเมนและข้อ จำกัด การเข้าถึงของแลมบ์ดา
โดเมนคือขอบเขตและพารามิเตอร์ในรายการพารามิเตอร์ในนิพจน์แลมบ์ดานั้นถูกต้องภายในขอบเขตของนิพจน์แลมบ์ดา (โดเมน) ในนิพจน์แลมบ์ดาสามารถเข้าถึงตัวแปรภายนอกได้: ตัวแปรท้องถิ่นตัวแปรคลาสและตัวแปรคงที่ แต่ระดับของข้อ จำกัด การดำเนินการแตกต่างกัน
เข้าถึงตัวแปรท้องถิ่น
ตัวแปรท้องถิ่นนอกนิพจน์แลมบ์ดาจะถูกรวบรวมโดย JVM เป็นประเภทสุดท้ายดังนั้นพวกเขาจึงสามารถเข้าถึงได้ แต่ไม่ได้รับการแก้ไข
การอ้างอิงคลาสสาธารณะ {โมฆะสาธารณะคงที่หลัก (สตริง [] args) {int n = 3; คำนวณคำนวณ = param -> {// n = 10; คอมไพล์ข้อผิดพลาดส่งคืน n + param; - คำนวณคำนวณ (10); } @functionalInterface อินเตอร์เฟสคำนวณ {int calculate (ค่า int); -เข้าถึงตัวแปรคงที่และสมาชิก
ภายในการแสดงออกของแลมบ์ดาตัวแปรคงที่และสมาชิกสามารถอ่านได้และเขียนได้
การอ้างอิงชั้นเรียนสาธารณะ {จำนวน int สาธารณะ = 1; สาธารณะคงที่ int num = 2; การทดสอบโมฆะสาธารณะ () {คำนวณการคำนวณ = param -> {num = 10; // แก้ไขจำนวนตัวแปรคงที่ = 3; // แก้ไขตัวแปรสมาชิกส่งคืน n + param; - คำนวณคำนวณ (10); } โมฆะคงที่สาธารณะหลัก (สตริง [] args) {} @functionalinterface อินเตอร์เฟสคำนวณ {int calculate (ค่า int); -แลมบ์ดาไม่สามารถเข้าถึงวิธีการเริ่มต้นของฟังก์ชันอินเตอร์เฟส
Java8 ปรับปรุงอินเทอร์เฟซรวมถึงวิธีเริ่มต้นที่สามารถเพิ่มคำจำกัดความคำหลักเริ่มต้นลงในอินเตอร์เฟส เราต้องทราบที่นี่ว่าการเข้าถึงวิธีการเริ่มต้นไม่สนับสนุนภายใน
การปฏิบัติของแลมบ์ดา
ในส่วน [ฟังก์ชันอินเทอร์เฟซ] [2] เรากล่าวว่าอินเทอร์เฟซการทำงานจำนวนมากถูกสร้างขึ้นในแพ็คเกจ java.util.function และตอนนี้เราจะอธิบายอินเทอร์เฟซการใช้งานที่ใช้กันทั่วไป
อินเทอร์เฟซ
ป้อนพารามิเตอร์และส่งคืนค่า Boolean ซึ่งมีวิธีการเริ่มต้นมากมายสำหรับการตัดสินเชิงตรรกะ:
@Test โมฆะสาธารณะ PredictTest () {Predicate <String> Predict = (S) -> S.Length ()> 0; การทดสอบบูลีน = predict.test ("ทดสอบ"); System.out.println ("ความยาวสตริงมากกว่า 0:" + ทดสอบ); ทดสอบ = predict.test (""); System.out.println ("ความยาวสตริงมากกว่า 0:" + ทดสอบ); เพรดิเคต <อ็อบเจิยง> pre = วัตถุ :: nonnull; วัตถุ OB = NULL; ทดสอบ = pre.test (ob); System.out.println ("วัตถุไม่ว่างเปล่า:" + ทดสอบ); ob = วัตถุใหม่ (); ทดสอบ = pre.test (ob); System.out.println ("วัตถุไม่ว่างเปล่า:" + ทดสอบ); -อินเตอร์เฟสฟังก์ชั่น
รับพารามิเตอร์และส่งคืนผลลัพธ์เดียว วิธีเริ่มต้น ( andThen ) สามารถสตริงหลายฟังก์ชั่นเข้าด้วยกันเพื่อสร้าง Funtion คอมโพสิต (พร้อมอินพุต, เอาต์พุต) ผลลัพธ์
@Test โมฆะสาธารณะฟังก์ชั่น FunctionTest () {ฟังก์ชัน <String, Integer> toInteger = Integer :: valueof; // ผลการดำเนินการของ tointeger ถูกใช้เป็นอินพุตไปยังฟังก์ชัน backtoString ที่สอง <สตริง, สตริง> backToString = toInteger.andthen (String :: valueof); สตริงผลลัพธ์ = backtoString.apply ("1234"); System.out.println (ผลลัพธ์); ฟังก์ชั่น <จำนวนเต็ม, จำนวนเต็ม> add = (i) -> {system.out.println ("อินพุต Frist:" + i); กลับฉัน * 2; - ฟังก์ชั่น <จำนวนเต็ม, จำนวนเต็ม> zero = add.andthen ((i) -> {system.out.println ("อินพุตที่สอง:" + i); return i * 0;}); จำนวนเต็ม res = zero.apply (8); System.out.println (res); -อินเทอร์เฟซซัพพลายเออร์
ส่งคืนผลลัพธ์ของประเภทที่กำหนด ซึ่งแตกต่างจาก Function Supplier ไม่จำเป็นต้องยอมรับพารามิเตอร์ (ซัพพลายเออร์ที่มีเอาต์พุต แต่ไม่มีอินพุต)
@Test โมฆะสาธารณะ suppLierTest () {ซัพพลายเออร์ <String> ซัพพลายเออร์ = () -> "ค่าประเภทพิเศษ"; String S = Supplier.get (); System.out.println (s); -อินเทอร์เฟซผู้บริโภค
แสดงถึงการดำเนินการที่จำเป็นต้องดำเนินการในพารามิเตอร์อินพุตเดียว ซึ่งแตกต่างจาก Function Consumer ไม่คืนค่า (ผู้บริโภคอินพุตไม่มีเอาต์พุต)
@Test โมฆะสาธารณะ consentest () {ผู้บริโภค <Integer> add5 = (p) -> {system.out.println ("ค่าเก่า:" + p); P = P + 5; System.out.println ("ค่าใหม่:" + p); - add5.accept (10); - การใช้อินเทอร์เฟซสี่ด้านบนแสดงถึงสี่ประเภทในแพ็คเกจ java.util.function หลังจากทำความเข้าใจกับอินเทอร์เฟซที่ใช้งานได้ทั้งสี่แล้วอินเทอร์เฟซอื่น ๆ จะเข้าใจได้ง่าย ตอนนี้มาสรุปง่ายๆ:
Predicate ใช้สำหรับการตัดสินเชิงตรรกะ Function ใช้ในสถานที่ที่มีอินพุตและเอาต์พุตซัพพลาย Supplier ถูกใช้ในสถานที่ที่ไม่มีอินพุตและเอาต์พุตและ Consumer ใช้ในสถานที่ที่มีอินพุตและไม่มีเอาต์พุต คุณสามารถรู้สถานการณ์การใช้งานตามความหมายของชื่อ
ลำธาร
แลมบ์ดานำการปิดสำหรับ Java 8 ซึ่งมีความสำคัญอย่างยิ่งในการดำเนินการรวบรวม: Java 8 สนับสนุนการทำงานของการทำงานบนกระแสของวัตถุคอลเลกชัน นอกจากนี้สตรีม API ยังรวมอยู่ในคอลเลกชัน API ซึ่งช่วยให้การดำเนินการแบบแบทช์บนวัตถุคอลเลกชัน
มาทำความรู้จักกับสตรีมกันเถอะ
สตรีมหมายถึงสตรีมข้อมูล ไม่มีโครงสร้างข้อมูลและไม่เก็บองค์ประกอบของตัวเอง การดำเนินการของมันจะไม่เปลี่ยนสตรีมต้นทาง แต่สร้างสตรีมใหม่ ในฐานะที่เป็นอินเทอร์เฟซสำหรับการใช้งานข้อมูลจะมีการกรองการเรียงลำดับการแมปและการควบคุม วิธีการเหล่านี้แบ่งออกเป็นสองประเภทตามประเภทการส่งคืน: วิธีการใด ๆ ที่ส่งคืนประเภทสตรีมเรียกว่าวิธีกลาง (การดำเนินการระดับกลาง) และส่วนที่เหลือเป็นวิธีการเสร็จสิ้น (การดำเนินการที่สมบูรณ์) วิธีการเสร็จสิ้นจะส่งคืนค่าบางประเภทในขณะที่วิธีการระดับกลางส่งคืนสตรีมใหม่ การเรียกใช้วิธีการระดับกลางมักจะถูกล่ามโซ่และกระบวนการจะเป็นไปป์ไลน์ เมื่อมีการเรียกวิธีสุดท้ายมันจะทำให้ค่าถูกบริโภคทันทีจากไปป์ไลน์ ที่นี่เราต้องจำไว้ว่าการดำเนินการสตรีมทำงานเป็น "ล่าช้า" ที่สุดเท่าที่จะเป็นไปได้ซึ่งเป็นสิ่งที่เรามักเรียกว่า "การดำเนินงานขี้เกียจ" ซึ่งจะช่วยลดการใช้ทรัพยากรและปรับปรุงประสิทธิภาพ สำหรับการดำเนินการระดับกลางทั้งหมด (ยกเว้นเรียงลำดับ) พวกเขาจะทำงานในโหมดการหน่วงเวลา
สตรีมไม่เพียง แต่ให้ความสามารถในการทำงานของข้อมูลที่มีประสิทธิภาพ แต่ที่สำคัญกว่านั้นคือสตรีมรองรับทั้งแบบอนุกรมและแบบขนาน Parallelism ช่วยให้สตรีมมีประสิทธิภาพที่ดีขึ้นในโปรเซสเซอร์หลายคอร์
กระบวนการใช้งานของสตรีมมีรูปแบบคงที่:
1. สร้างสตรีม
2. ผ่านการดำเนินการระดับกลาง "เปลี่ยน" สตรีมดั้งเดิมและสร้างสตรีมใหม่
3. ใช้การดำเนินการเสร็จสมบูรณ์เพื่อสร้างผลลัพธ์สุดท้าย
นั่นคือ
สร้าง -> เปลี่ยน -> เสร็จสมบูรณ์
การสร้างกระแส
สำหรับคอลเลกชันสามารถสร้างขึ้นได้โดยเรียก stream() หรือ parallelStream() นอกจากนี้ยังมีการใช้วิธีการทั้งสองนี้ในส่วนต่อประสานการรวบรวม สำหรับอาร์เรย์สามารถสร้างได้โดยวิธีการคงที่ of(T … values) นอกจากนี้อาร์เรย์ยังให้การสนับสนุนสตรีม
นอกเหนือจากการสร้างสตรีมตามคอลเลกชันหรืออาร์เรย์ด้านบนคุณยังสามารถสร้างสตรีมที่ว่างเปล่าผ่าน Steam.empty() หรือใช้ generate() เพื่อสร้างสตรีมที่ไม่มีที่สิ้นสุด
ลองใช้สตรีมแบบอนุกรมเพื่อแสดงให้เห็นถึงวิธีการสตรีมระดับกลางและวิธีการเสร็จสมบูรณ์หลายวิธี ก่อนสร้างรายการคอลเลกชัน:
รายการ <String> list = new ArrayList <String> (); lists.add ("A1"); lists.add ("A2"); lists.add ("b1"); lists.add ("b2"); lists.add ("b3"); lists.add ("O1");วิธีกลาง
กรอง
เมื่อรวมกับอินเทอร์เฟซภาคแสดงตัวกรองตัวกรององค์ประกอบทั้งหมดในวัตถุสตรีมมิ่ง การดำเนินการนี้เป็นการดำเนินการระดับกลางซึ่งหมายความว่าคุณสามารถดำเนินการอื่น ๆ ตามผลลัพธ์ที่ส่งคืนโดยการดำเนินการ
โมฆะสาธารณะคงที่ StreamFilterTest () {lists.stream (). ตัวกรอง ((s -> s.startswith ("a"))). foreach (system.out :: println); // เทียบเท่ากับการดำเนินการด้านบน Predicate <String> Predicate = (S) -> S.Startswith ("A"); lists.stream (). ตัวกรอง (ภาคแสดง). foreach (system.out :: println); // การกรองอย่างต่อเนื่อง Predicate <String> predicate1 = (s -> s.endswith ("1")); lists.stream (). ตัวกรอง (ภาคแสดง) -เรียงลำดับ (เรียงลำดับ)
เมื่อรวมกับอินเทอร์เฟซเปรียบเทียบการดำเนินการนี้จะส่งคืนมุมมองของสตรีมที่เรียงลำดับและลำดับของสตรีมดั้งเดิมจะไม่เปลี่ยนแปลง กฎการเปรียบเทียบจะถูกระบุผ่านตัวเปรียบเทียบและค่าเริ่มต้นคือการเรียงลำดับตามลำดับตามธรรมชาติ
โมฆะคงที่สาธารณะ StreamSortedTest () {System.out.println ("ตัวเปรียบเทียบเริ่มต้น"); lists.stream (). เรียงลำดับ (). ตัวกรอง ((s -> s.startswith ("a"))). foreach (system.out :: println); System.out.println ("ตัวเปรียบเทียบที่กำหนดเอง"); lists.stream (). เรียงลำดับ ((p1, p2) -> p2.compareto (p1)). ตัวกรอง ((s -> s.startswith ("a"))). foreach (system.out :: println); -แผนที่ (แผนที่)
เมื่อรวมกับอินเทอร์เฟซ Function ก์ชั่นการดำเนินการนี้สามารถแมปแต่ละองค์ประกอบในวัตถุสตรีมลงในองค์ประกอบอื่นโดยตระหนักถึงการแปลงประเภทองค์ประกอบ
โมฆะคงที่สาธารณะ streammaptest () {lists.stream (). แผนที่ (String :: touppercase) .sorted ((a, b) -> b.Compareto (a)). foreach (system.out :: println); System.out.println ("กฎการแมปแบบกำหนดเอง"); ฟังก์ชั่น <สตริง, สตริง> function = (p) -> {return p + ".txt"; - lists.stream (). แผนที่ (String :: touppercase) .map (ฟังก์ชั่น) .sorted ((a, b) -> b.Compareto (a)). foreach (system.out :: println); -ข้างต้นแนะนำการดำเนินการที่ใช้กันทั่วไปสามครั้งโดยย่อซึ่งทำให้การประมวลผลของคอลเลกชันง่ายขึ้นอย่างมาก ต่อไปเราจะแนะนำวิธีที่จะทำให้เสร็จสมบูรณ์:
วิธีการตกแต่ง
หลังจากกระบวนการ "แปลง" จะต้องได้รับผลลัพธ์นั่นคือการดำเนินการเสร็จสมบูรณ์ มาดูการดำเนินการที่เกี่ยวข้องด้านล่าง:
จับคู่
ใช้เพื่อตรวจสอบว่า predicate ตรงกับวัตถุสตรีมหรือไม่และในที่สุดก็ส่งคืนผลลัพธ์ประเภท Boolean ตัวอย่างเช่น:
โมฆะคงที่สาธารณะ StreamMatchTest () {// ส่งคืนจริงตราบใดที่องค์ประกอบหนึ่งในวัตถุสตรีมตรงกับบูลีน anystartwitha = lists.stream (). anymatch ((s -> s.startswith ("a"))); System.out.println (Anystartwitha); // ส่งคืนจริงเมื่อแต่ละองค์ประกอบในวัตถุสตรีมตรงกับบูลีน allstartwitha = lists.stream (). allmatch ((s -> s.startswith ("a"))); System.out.println (Allstartwitha); -เก็บรวบรวม
หลังจากการเปลี่ยนแปลงเรารวบรวมองค์ประกอบของกระแสที่ถูกแปลงเช่นการบันทึกองค์ประกอบเหล่านี้ลงในคอลเลกชัน ในเวลานี้เราสามารถใช้วิธีการรวบรวมที่จัดทำโดยสตรีมตัวอย่างเช่น:
โมฆะสาธารณะคงที่ StreamCollectTest () {รายการ <String> list = lists.stream (). ตัวกรอง ((p) -> p.startswith ("a")). เรียงลำดับ (). collect (collectors.tolist ()); System.out.println (รายการ); -นับ
จำนวน SQL-like ใช้เพื่อนับจำนวนองค์ประกอบทั้งหมดในสตรีมตัวอย่างเช่น:
โมฆะคงที่สาธารณะ StreamCountTest () {long count = lists.stream (). ตัวกรอง ((s -> s.startswith ("a"))). count (); System.out.println (นับ); -ลด
วิธี reduce ช่วยให้เราสามารถคำนวณองค์ประกอบในแบบของเราเองหรือเชื่อมโยงองค์ประกอบในสตรีมที่มีรูปแบบบางอย่างเช่น:
โมฆะสาธารณะคงที่ streamreducetest () {ตัวเลือก <String> onftional = lists.stream (). เรียงลำดับ () ลดลง ((S1, S2) -> {System.out.println (S1 + "|" + S2); ส่งคืน S1 + "|" + S2;}); -ผลการดำเนินการมีดังนี้:
A1 | A2A1 | A2 | B1A1 | A2 | B1 | B2A1 | A2 | B1 | B2 | B3A1 | A2 | B1 | B2 | B3 | O1
สตรีมแบบขนานกับสตรีมอนุกรม
จนถึงตอนนี้เราได้แนะนำการดำเนินงานระดับกลางและการดำเนินการที่เสร็จสมบูรณ์ แน่นอนตัวอย่างทั้งหมดจะขึ้นอยู่กับสตรีมอนุกรม ต่อไปเราจะแนะนำละครคีย์ - สตรีมแบบขนาน (สตรีมแบบขนาน) สตรีมแบบขนานถูกนำมาใช้ตามกรอบการสลายตัวแบบขนานของส้อม-เข้าร่วมและแบ่งข้อมูลขนาดใหญ่ที่ตั้งไว้เป็นข้อมูลขนาดเล็กหลายตัวและส่งไปยังเธรดที่แตกต่างกันสำหรับการประมวลผล ด้วยวิธีนี้ประสิทธิภาพจะได้รับการปรับปรุงอย่างมากภายใต้สถานการณ์ของการประมวลผลแบบหลายคอร์ สิ่งนี้สอดคล้องกับแนวคิดการออกแบบของ MapReduce: งานขนาดใหญ่มีขนาดเล็กลงและงานเล็ก ๆ จะถูกกำหนดใหม่ให้กับเครื่องจักรที่แตกต่างกันเพื่อดำเนินการ แต่งานเล็ก ๆ ที่นี่ถูกส่งมอบให้กับโปรเซสเซอร์ที่แตกต่างกัน
สร้างสตรีมแบบขนานผ่าน parallelStream() ในการตรวจสอบว่าสตรีมแบบขนานสามารถปรับปรุงประสิทธิภาพได้จริงหรือไม่เราเรียกใช้รหัสทดสอบต่อไปนี้:
ก่อนอื่นสร้างคอลเลกชันขนาดใหญ่:
รายการ <String> biglists = new ArrayList <> (); สำหรับ (int i = 0; i <10000000; i ++) {uuid uuid = uuid.randomuuid (); biglists.add (uuid.toString ()); -ทดสอบเวลาในการเรียงลำดับภายใต้สตรีมอนุกรม:
โมฆะคงที่ส่วนตัว NotparallelsreamsortedTest (รายการ <String> BigLists) {Long StartTime = System.Nanotime (); Long count = biglists.stream (). sorted (). count (); endtime long = system.nanotime (); Long Millis = TimeUnit.nanoseconds.tomillis (Endtime - StartTime); System.out.println (System.out.printf ("Serial Sort: %D MS", Millis)); -ทดสอบเวลาในการเรียงลำดับในสตรีมแบบขนาน:
โมฆะแบบคงที่ส่วนตัว ParalleLsreamSortedTest (รายการ <String> BigLists) {Long StartTime = System.Nanotime (); Long count = biglists.parallelStream (). sorted (). count (); endtime long = system.nanotime (); Long Millis = TimeUnit.nanoseconds.tomillis (Endtime - StartTime); System.out.println (System.out.printf ("perallelesorting: %d ms", millis)); -ผลลัพธ์มีดังนี้:
เรียงลำดับอนุกรม: 13336 ms
การเรียงลำดับแบบขนาน: 6755 ms
หลังจากเห็นสิ่งนี้เราพบว่าประสิทธิภาพดีขึ้นประมาณ 50% คุณคิดว่าคุณจะสามารถใช้ parallel Stream ในอนาคตได้หรือไม่? ในความเป็นจริงมันไม่ใช่กรณี หากคุณยังคงเป็นโปรเซสเซอร์หลักเดียวในขณะนี้และปริมาณข้อมูลไม่ใหญ่การสตรีมอนุกรมยังคงเป็นทางเลือกที่ดี คุณจะพบว่าในบางกรณีประสิทธิภาพของสตรีมอนุกรมดีกว่า สำหรับการใช้งานเฉพาะคุณต้องทดสอบก่อนแล้วตัดสินใจตามสถานการณ์จริง
การดำเนินการขี้เกียจ
ข้างต้นเราได้พูดคุยเกี่ยวกับสตรีมที่ทำงานช้าที่สุดเท่าที่จะเป็นไปได้และที่นี่เราอธิบายโดยการสร้างสตรีมที่ไม่มีที่สิ้นสุด:
ก่อนอื่นให้ใช้วิธี generate สตรีมเพื่อสร้างลำดับตัวเลขธรรมชาติจากนั้นแปลงกระแสผ่าน map :
// การเพิ่มลำดับคลาส Natureseq ใช้ซัพพลายเออร์ <lond> {ค่ายาว = 0; @Override สาธารณะ Long Get () {value ++; ค่าส่งคืน; }} โมฆะสาธารณะ streamCreateTest () {สตรีม <lont> stream = stream.generate (ใหม่ natureseq ()); System.out.println ("จำนวนองค์ประกอบ:"+stream.map ((param) -> {return param;}). จำกัด (1,000) .count ()); -ผลการดำเนินการคือ:
จำนวนองค์ประกอบ: 1,000
เราพบว่าในตอนแรกการดำเนินการระดับกลางใด ๆ (เช่น filter,map ฯลฯ แต่ไม่สามารถ sorted ได้) ก็โอเค นั่นคือกระบวนการในการดำเนินการระดับกลางบนสตรีมและรอดชีวิตจากสตรีมใหม่จะไม่มีผลทันที (หรือการดำเนินการ map ในตัวอย่างนี้จะทำงานตลอดไปและถูกบล็อก) และสตรีมเริ่มคำนวณเมื่อวิธีการเสร็จสิ้น ด้วยวิธี limit() ให้แปลงสตรีมที่ไม่มีที่สิ้นสุดนี้เป็นสตรีม จำกัด
สรุป
ข้างต้นคือเนื้อหาทั้งหมดของการแนะนำอย่างรวดเร็วเกี่ยวกับ Java Lambda หลังจากอ่านบทความนี้แล้วคุณมีความเข้าใจอย่างลึกซึ้งเกี่ยวกับ Java Lambda หรือไม่? ฉันหวังว่าบทความนี้จะเป็นประโยชน์กับทุกคนในการเรียนรู้ Java Lambda