İşte geçen gün karşılaştığım başka bir teknik:
Collections.nCopies(8, 1)
.stream()
.forEach(i -> System.out.println(i));
Collections.nCopies
Arama bir oluşturur List
içeren n
Verdiğiniz her türlü değer kopyalarını. Bu durumda kutulu Integer
değer 1'dir. Elbette aslında n
öğeler içeren bir liste oluşturmaz ; yalnızca değeri ve uzunluğu içeren "sanallaştırılmış" bir liste oluşturur ve get
aralık içindeki herhangi bir çağrı yalnızca değeri döndürür. nCopies
Koleksiyonlar Çerçeve JDK 1.2 yolu geri tanıtıldı beri yöntem civarında olmuştur. Elbette, sonucundan bir akış oluşturma yeteneği Java SE 8'e eklendi.
Büyük bir anlaşma, aynı şeyi aynı sayıda satırda yapmanın başka bir yolu.
Bununla birlikte, bu teknik IntStream.generate
ve IntStream.iterate
yaklaşımlarından daha hızlıdır ve şaşırtıcı bir şekilde yaklaşımdan da daha hızlıdır IntStream.range
.
İçin iterate
ve generate
sonuç belki de şaşırtıcı değildir. Akış çerçevesi (gerçekten, bu akışlar için Ayırıcılar), lambdaların potansiyel olarak her seferinde farklı değerler üreteceği ve sınırsız sayıda sonuç üreteceği varsayımı üzerine inşa edilmiştir. Bu, paralel bölmeyi özellikle zorlaştırır. iterate
Her çağrı öncekinin sonucunu gerektirdiğinden yöntem de bu durum için problemlidir. Bu nedenle, kullanılan generate
ve iterate
tekrarlanan sabitler oluşturmak için çok iyi olmayan akımlar .
Nispeten düşük performans range
şaşırtıcı. Bu da sanallaştırılmıştır, bu nedenle elemanların tamamı bellekte mevcut değildir ve boyutu önceden bilinir. Bu, hızlı ve kolayca paralelleştirilebilir bir ayırıcı yapmalıdır. Ama şaşırtıcı bir şekilde pek de başarılı olmadı. Belki de nedeni, range
aralığın her bir öğesi için bir değer hesaplamak ve ardından bunun üzerinde bir işlev çağırmak zorunda olmasıdır. Ancak bu işlev girdisini görmezden gelir ve bir sabit döndürür, bu yüzden bunun satır içi ve öldürülmemiş olmasına şaşırdım.
Collections.nCopies
Teknik hiçbir ilkel uzmanlık olmadığından, değerlerini işlemek amacıyla unboxing / boks yapmak zorunda List
. Değer her seferinde aynı olduğundan, temelde bir kez kutulanır ve bu kutu tüm n
kopyalar tarafından paylaşılır . Kutulama / kutudan çıkarma işleminin son derece optimize edildiğinden, hatta içselleştirildiğinden şüpheleniyorum ve iyi bir şekilde satır içine alınabilir.
İşte kod:
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();
}
Ve işte JMH sonuçları: (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 sürümünde oldukça fazla farklılık var, ancak genel olarak rahat bir şekilde 20 kat daha hızlı görünüyor. (Yine de yanlış bir şey yaptığıma inanmaya oldukça istekli olurum.)
nCopies
Tekniğin ne kadar iyi çalıştığına şaşırdım . Dahili olarak çok özel bir şey yapmaz, sanallaştırılmış listenin akışı basitçe kullanılarak gerçekleştirilir IntStream.range
! Bunun hızlı ilerlemesi için özel bir ayırıcı yaratmanın gerekli olacağını ummuştum, ama şimdiden oldukça iyi görünüyor.