นี่เป็นอีกเทคนิคหนึ่งที่ฉันวิ่งข้ามวัน:
Collections.nCopies(8, 1)
.stream()
.forEach(i -> System.out.println(i));
Collections.nCopies
โทรสร้างList
ที่มีn
สำเนาของสิ่งที่คุณให้คุ้มค่า ในกรณีนี้เป็นInteger
ค่าบรรจุกล่อง1 แน่นอนว่ามันไม่ได้สร้างรายการที่มีn
องค์ประกอบ จะสร้างรายการ "virtualized" ที่มีเฉพาะค่าและความยาวและการเรียกไปยังget
ภายในช่วงจะส่งกลับค่า nCopies
วิธีการได้รับรอบตั้งแต่คอลเลกชันกรอบได้รับการแนะนำทางกลับใน JDK 1.2 แน่นอนความสามารถในการสร้างสตรีมจากผลลัพธ์ได้ถูกเพิ่มใน Java SE 8
เรื่องใหญ่อีกวิธีหนึ่งในการทำสิ่งเดียวกันในจำนวนบรรทัดเดียวกัน
แต่เทคนิคนี้จะเร็วกว่าIntStream.generate
และIntStream.iterate
วิธีการและที่น่าแปลกใจก็ยังเร็วกว่าIntStream.range
วิธีการ
สำหรับiterate
และgenerate
ผลลัพธ์อาจไม่น่าแปลกใจเกินไป เฟรมเวิร์กของสตรีม (จริงๆคือ Spliterators สำหรับสตรีมเหล่านี้) สร้างขึ้นจากสมมติฐานที่ว่า lambdas อาจสร้างค่าที่แตกต่างกันในแต่ละครั้งและจะสร้างผลลัพธ์ที่ไม่ถูกผูกมัด ทำให้การแยกขนานยากเป็นพิเศษ iterate
วิธีการยังเป็นปัญหาสำหรับกรณีนี้เพราะการโทรแต่ละครั้งต้องมีผลมาจากการอย่างใดอย่างหนึ่งก่อนหน้านี้ ดังนั้นสตรีมที่ใช้generate
และiterate
ทำได้ไม่ดีนักในการสร้างค่าคงที่ซ้ำ ๆ
ประสิทธิภาพที่ค่อนข้างแย่range
เป็นที่น่าแปลกใจ สิ่งนี้ก็เสมือนจริงเช่นกันดังนั้นองค์ประกอบทั้งหมดจึงไม่มีอยู่ในหน่วยความจำจริง ๆ และขนาดจะถูกรู้ล่วงหน้า สิ่งนี้ควรทำให้ตัวแยกสัญญาณที่รวดเร็วและขนานกันได้ง่าย แต่น่าแปลกใจที่ทำได้ไม่ดีนัก บางทีเหตุผลก็คือrange
ต้องคำนวณค่าสำหรับแต่ละองค์ประกอบของช่วงแล้วเรียกใช้ฟังก์ชัน แต่ฟังก์ชั่นนี้ไม่สนใจอินพุตของมันและส่งกลับค่าคงที่ดังนั้นฉันจึงแปลกใจที่นี่ไม่อินไลน์และถูกฆ่า
Collections.nCopies
เทคนิคมีการทำมวย / unboxing List
เพื่อจัดการค่าเนื่องจากไม่มีความเชี่ยวชาญดั้งเดิมของ เนื่องจากค่าเหมือนกันทุกครั้งโดยทั่วไปจะใส่กล่องครั้งเดียวและกล่องนั้นจะแชร์โดยn
สำเนาทั้งหมด ฉันสงสัยว่าการชกมวย / การแกะกล่องนั้นได้รับการปรับให้เหมาะสมอย่างมากแม้จะอยู่ภายในและสามารถสอดใส่ได้ดี
นี่คือรหัส:
public static final int LIMIT = 500_000_000;
public static final long VALUE = 3L;
public long range() {
return
LongStream.range(0, LIMIT)
.parallel()
.map(i -> VALUE)
.map(i -> i % 73 % 13)
.sum();
}
public long ncopies() {
return
Collections.nCopies(LIMIT, VALUE)
.parallelStream()
.mapToLong(i -> i)
.map(i -> i % 73 % 13)
.sum();
}
และนี่คือผลลัพธ์ JMH: (2.8GHz Core2Duo)
Benchmark Mode Samples Mean Mean error Units
c.s.q.SO18532488.ncopies thrpt 5 7.547 2.904 ops/s
c.s.q.SO18532488.range thrpt 5 0.317 0.064 ops/s
มีความแปรปรวนอยู่พอสมควรในเวอร์ชัน ncopies แต่โดยรวมแล้วดูเหมือนว่าเร็วกว่ารุ่น range ถึง 20 เท่า (ฉันค่อนข้างเต็มใจที่จะเชื่อว่าฉันทำอะไรผิดพลาด)
ฉันประหลาดใจที่nCopies
เทคนิคนี้ทำงานได้ดีเพียงใด ภายในไม่ได้ทำอะไรพิเศษมากนักด้วยสตรีมของรายการเวอร์ชวลไลซ์เพียงแค่ใช้งานIntStream.range
! ฉันคาดไว้ว่าจะต้องสร้างตัวแยกเฉพาะเพื่อให้มันทำงานได้เร็ว แต่ดูเหมือนว่าจะดีอยู่แล้ว