Pekala, bunu rahatlatmak için birkaç senaryo çalıştırmak ve sonuçların bazı görselleştirmelerini almak için bir test uygulaması oluşturdum. Testler şu şekilde yapılır:
- Bir dizi farklı koleksiyon boyutu denendi: yüz, bin ve yüz bin giriş.
- Kullanılan anahtarlar, bir kimlik ile benzersiz şekilde tanımlanan bir sınıfın örnekleridir. Her test, kimlik olarak artan tam sayılarla benzersiz anahtarlar kullanır.
equals
Bir anahtar haritalama başka bir yazar, böylece yöntem sadece bir kimlik kullanır.
- Anahtarlar, bazı ön ayar numaralarına karşı ID'lerinin modül kalanından oluşan bir karma kod alır. Bu numaraya hash limiti diyeceğiz . Bu, beklenen hash çarpışmalarının sayısını kontrol etmeme izin verdi. Örneğin, koleksiyon boyutumuz 100 ise, 0 ile 99 arasında değişen kimliklere sahip anahtarlarımız olacaktır. Karma sınırı 100 ise, her anahtarın benzersiz bir karma kodu olacaktır. Karma sınırı 50 ise, anahtar 0, anahtar 50 ile aynı karma koduna sahip olacaktır; 1, 51 ile aynı karma koduna sahip olacaktır. Diğer bir deyişle, anahtar başına beklenen karma çarpışma sayısı, koleksiyon boyutunun karma değerine bölünmesiyle hesaplanır. limit.
- Koleksiyon boyutu ve hash limitinin her kombinasyonu için, testi farklı ayarlarla başlatılan hash haritalarını kullanarak çalıştırıyorum. Bu ayarlar yük faktörü ve toplama ayarının bir faktörü olarak ifade edilen bir başlangıç kapasitesidir. Örneğin, toplama boyutu 100 ve başlangıç kapasite faktörü 1,25 olan bir test, başlangıç kapasitesi 125 olan bir karma haritayı başlatacaktır.
- Her anahtarın değeri sadece yenidir
Object
.
- Her test sonucu, bir Result sınıfının bir örneğinde kapsüllenir. Tüm testlerin sonunda sonuçlar en kötü genel performanstan en iyiye doğru sıralanır.
- Ortalama koyma ve alma süresi, 10 koyma / alma başına hesaplanır.
- JIT derleme etkisini ortadan kaldırmak için tüm test kombinasyonları bir kez çalıştırılır. Bundan sonra, gerçek sonuçlar için testler yapılır.
İşte sınıf:
package hashmaptest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
public class HashMapTest {
private static final List<Result> results = new ArrayList<Result>();
public static void main(String[] args) throws IOException {
final int[][] sampleSizesAndHashLimits = new int[][] {
{100, 50, 90, 100},
{1000, 500, 900, 990, 1000},
{100000, 10000, 90000, 99000, 100000}
};
final double[] initialCapacityFactors = new double[] {0.5, 0.75, 1.0, 1.25, 1.5, 2.0};
final float[] loadFactors = new float[] {0.5f, 0.75f, 1.0f, 1.25f};
for(int[] sizeAndLimits : sampleSizesAndHashLimits) {
int size = sizeAndLimits[0];
for(int i = 1; i < sizeAndLimits.length; ++i) {
int limit = sizeAndLimits[i];
for(double initCapacityFactor : initialCapacityFactors) {
for(float loadFactor : loadFactors) {
runTest(limit, size, initCapacityFactor, loadFactor);
}
}
}
}
results.clear();
for(int[] sizeAndLimits : sampleSizesAndHashLimits) {
int size = sizeAndLimits[0];
for(int i = 1; i < sizeAndLimits.length; ++i) {
int limit = sizeAndLimits[i];
for(double initCapacityFactor : initialCapacityFactors) {
for(float loadFactor : loadFactors) {
runTest(limit, size, initCapacityFactor, loadFactor);
}
}
}
}
Collections.sort(results);
for(final Result result : results) {
result.printSummary();
}
}
private static void runTest(final int hashLimit, final int sampleSize,
final double initCapacityFactor, final float loadFactor) {
final int initialCapacity = (int)(sampleSize * initCapacityFactor);
System.out.println("Running test for a sample collection of size " + sampleSize
+ ", an initial capacity of " + initialCapacity + ", a load factor of "
+ loadFactor + " and keys with a hash code limited to " + hashLimit);
System.out.println("====================");
double hashOverload = (((double)sampleSize/hashLimit) - 1.0) * 100.0;
System.out.println("Hash code overload: " + hashOverload + "%");
final List<Key> keys = generateSamples(hashLimit, sampleSize);
final List<Object> values = generateValues(sampleSize);
final HashMap<Key, Object> map = new HashMap<Key, Object>(initialCapacity, loadFactor);
final long startPut = System.nanoTime();
for(int i = 0; i < sampleSize; ++i) {
map.put(keys.get(i), values.get(i));
}
final long endPut = System.nanoTime();
final long putTime = endPut - startPut;
final long averagePutTime = putTime/(sampleSize/10);
System.out.println("Time to map all keys to their values: " + putTime + " ns");
System.out.println("Average put time per 10 entries: " + averagePutTime + " ns");
final long startGet = System.nanoTime();
for(int i = 0; i < sampleSize; ++i) {
map.get(keys.get(i));
}
final long endGet = System.nanoTime();
final long getTime = endGet - startGet;
final long averageGetTime = getTime/(sampleSize/10);
System.out.println("Time to get the value for every key: " + getTime + " ns");
System.out.println("Average get time per 10 entries: " + averageGetTime + " ns");
System.out.println("");
final Result result =
new Result(sampleSize, initialCapacity, loadFactor, hashOverload, averagePutTime, averageGetTime, hashLimit);
results.add(result);
System.gc();
try {
Thread.sleep(200);
} catch(final InterruptedException e) {}
}
private static List<Key> generateSamples(final int hashLimit, final int sampleSize) {
final ArrayList<Key> result = new ArrayList<Key>(sampleSize);
for(int i = 0; i < sampleSize; ++i) {
result.add(new Key(i, hashLimit));
}
return result;
}
private static List<Object> generateValues(final int sampleSize) {
final ArrayList<Object> result = new ArrayList<Object>(sampleSize);
for(int i = 0; i < sampleSize; ++i) {
result.add(new Object());
}
return result;
}
private static class Key {
private final int hashCode;
private final int id;
Key(final int id, final int hashLimit) {
this.id = id;
this.hashCode = id % hashLimit;
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(final Object o) {
return ((Key)o).id == this.id;
}
}
static class Result implements Comparable<Result> {
final int sampleSize;
final int initialCapacity;
final float loadFactor;
final double hashOverloadPercentage;
final long averagePutTime;
final long averageGetTime;
final int hashLimit;
Result(final int sampleSize, final int initialCapacity, final float loadFactor,
final double hashOverloadPercentage, final long averagePutTime,
final long averageGetTime, final int hashLimit) {
this.sampleSize = sampleSize;
this.initialCapacity = initialCapacity;
this.loadFactor = loadFactor;
this.hashOverloadPercentage = hashOverloadPercentage;
this.averagePutTime = averagePutTime;
this.averageGetTime = averageGetTime;
this.hashLimit = hashLimit;
}
@Override
public int compareTo(final Result o) {
final long putDiff = o.averagePutTime - this.averagePutTime;
final long getDiff = o.averageGetTime - this.averageGetTime;
return (int)(putDiff + getDiff);
}
void printSummary() {
System.out.println("" + averagePutTime + " ns per 10 puts, "
+ averageGetTime + " ns per 10 gets, for a load factor of "
+ loadFactor + ", initial capacity of " + initialCapacity
+ " for " + sampleSize + " mappings and " + hashOverloadPercentage
+ "% hash code overload.");
}
}
}
Bunu çalıştırmak biraz zaman alabilir. Sonuçlar standart olarak yazdırılır. Bir satır yorum yaptığımı fark edebilirsiniz. Bu satır, sonuçların görsel temsillerini png dosyalarına çıkaran bir görselleştirici çağırır. Bunun için sınıf aşağıda verilmiştir. Çalıştırmak istiyorsanız, yukarıdaki koddaki uygun satırın açıklamasını kaldırın. Dikkat edin: görselleştirici sınıfı Windows üzerinde çalıştığınızı varsayar ve C: \ temp'de klasörler ve dosyalar oluşturur. Başka bir platformda çalışırken bunu ayarlayın.
package hashmaptest;
import hashmaptest.HashMapTest.Result;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.imageio.ImageIO;
public class ResultVisualizer {
private static final Map<Integer, Map<Integer, Set<Result>>> sampleSizeToHashLimit =
new HashMap<Integer, Map<Integer, Set<Result>>>();
private static final DecimalFormat df = new DecimalFormat("0.00");
static void visualizeResults(final List<Result> results) throws IOException {
final File tempFolder = new File("C:\\temp");
final File baseFolder = makeFolder(tempFolder, "hashmap_tests");
long bestPutTime = -1L;
long worstPutTime = 0L;
long bestGetTime = -1L;
long worstGetTime = 0L;
for(final Result result : results) {
final Integer sampleSize = result.sampleSize;
final Integer hashLimit = result.hashLimit;
final long putTime = result.averagePutTime;
final long getTime = result.averageGetTime;
if(bestPutTime == -1L || putTime < bestPutTime)
bestPutTime = putTime;
if(bestGetTime <= -1.0f || getTime < bestGetTime)
bestGetTime = getTime;
if(putTime > worstPutTime)
worstPutTime = putTime;
if(getTime > worstGetTime)
worstGetTime = getTime;
Map<Integer, Set<Result>> hashLimitToResults =
sampleSizeToHashLimit.get(sampleSize);
if(hashLimitToResults == null) {
hashLimitToResults = new HashMap<Integer, Set<Result>>();
sampleSizeToHashLimit.put(sampleSize, hashLimitToResults);
}
Set<Result> resultSet = hashLimitToResults.get(hashLimit);
if(resultSet == null) {
resultSet = new HashSet<Result>();
hashLimitToResults.put(hashLimit, resultSet);
}
resultSet.add(result);
}
System.out.println("Best average put time: " + bestPutTime + " ns");
System.out.println("Best average get time: " + bestGetTime + " ns");
System.out.println("Worst average put time: " + worstPutTime + " ns");
System.out.println("Worst average get time: " + worstGetTime + " ns");
for(final Integer sampleSize : sampleSizeToHashLimit.keySet()) {
final File sizeFolder = makeFolder(baseFolder, "sample_size_" + sampleSize);
final Map<Integer, Set<Result>> hashLimitToResults =
sampleSizeToHashLimit.get(sampleSize);
for(final Integer hashLimit : hashLimitToResults.keySet()) {
final File limitFolder = makeFolder(sizeFolder, "hash_limit_" + hashLimit);
final Set<Result> resultSet = hashLimitToResults.get(hashLimit);
final Set<Float> loadFactorSet = new HashSet<Float>();
final Set<Integer> initialCapacitySet = new HashSet<Integer>();
for(final Result result : resultSet) {
loadFactorSet.add(result.loadFactor);
initialCapacitySet.add(result.initialCapacity);
}
final List<Float> loadFactors = new ArrayList<Float>(loadFactorSet);
final List<Integer> initialCapacities = new ArrayList<Integer>(initialCapacitySet);
Collections.sort(loadFactors);
Collections.sort(initialCapacities);
final BufferedImage putImage =
renderMap(resultSet, loadFactors, initialCapacities, worstPutTime, bestPutTime, false);
final BufferedImage getImage =
renderMap(resultSet, loadFactors, initialCapacities, worstGetTime, bestGetTime, true);
final String putFileName = "size_" + sampleSize + "_hlimit_" + hashLimit + "_puts.png";
final String getFileName = "size_" + sampleSize + "_hlimit_" + hashLimit + "_gets.png";
writeImage(putImage, limitFolder, putFileName);
writeImage(getImage, limitFolder, getFileName);
}
}
}
private static File makeFolder(final File parent, final String folder) throws IOException {
final File child = new File(parent, folder);
if(!child.exists())
child.mkdir();
return child;
}
private static BufferedImage renderMap(final Set<Result> results, final List<Float> loadFactors,
final List<Integer> initialCapacities, final float worst, final float best,
final boolean get) {
final Color[][] map = new Color[initialCapacities.size()][loadFactors.size()];
for(final Result result : results) {
final int x = initialCapacities.indexOf(result.initialCapacity);
final int y = loadFactors.indexOf(result.loadFactor);
final float time = get ? result.averageGetTime : result.averagePutTime;
final float score = (time - best)/(worst - best);
final Color c = new Color(score, 1.0f - score, 0.0f);
map[x][y] = c;
}
final int imageWidth = initialCapacities.size() * 40 + 50;
final int imageHeight = loadFactors.size() * 40 + 50;
final BufferedImage image =
new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_3BYTE_BGR);
final Graphics2D g = image.createGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, imageWidth, imageHeight);
for(int x = 0; x < map.length; ++x) {
for(int y = 0; y < map[x].length; ++y) {
g.setColor(map[x][y]);
g.fillRect(50 + x*40, imageHeight - 50 - (y+1)*40, 40, 40);
g.setColor(Color.BLACK);
g.drawLine(25, imageHeight - 50 - (y+1)*40, 50, imageHeight - 50 - (y+1)*40);
final Float loadFactor = loadFactors.get(y);
g.drawString(df.format(loadFactor), 10, imageHeight - 65 - (y)*40);
}
g.setColor(Color.BLACK);
g.drawLine(50 + (x+1)*40, imageHeight - 50, 50 + (x+1)*40, imageHeight - 15);
final int initialCapacity = initialCapacities.get(x);
g.drawString(((initialCapacity%1000 == 0) ? "" + (initialCapacity/1000) + "K" : "" + initialCapacity), 15 + (x+1)*40, imageHeight - 25);
}
g.drawLine(25, imageHeight - 50, imageWidth, imageHeight - 50);
g.drawLine(50, 0, 50, imageHeight - 25);
g.dispose();
return image;
}
private static void writeImage(final BufferedImage image, final File folder,
final String filename) throws IOException {
final File imageFile = new File(folder, filename);
ImageIO.write(image, "png", imageFile);
}
}
Görselleştirilmiş çıktı aşağıdaki gibidir:
- Testler önce toplama boyutuna, ardından hash limitine göre bölünür.
- Her test için, ortalama koyma süresi (10 koyma başına) ve ortalama alma süresi (10 alma başına) ile ilgili bir çıktı görüntüsü vardır. Görüntüler, başlangıç kapasitesi ve yük faktörü kombinasyonu başına bir renk gösteren iki boyutlu "ısı haritaları" dır.
- Görüntülerdeki renkler, doymuş yeşilden doymuş kırmızıya değişen, en iyiden en kötü sonuca kadar normalleştirilmiş bir ölçekte ortalama süreye dayanmaktadır. Başka bir deyişle, en iyi zaman tamamen yeşil olurken, en kötü zaman tamamen kırmızı olacaktır. İki farklı zaman ölçümü asla aynı renge sahip olmamalıdır.
- Renk haritaları, satışlar için ayrı ayrı hesaplanır, ancak ilgili kategoriler için tüm testleri kapsar.
- Görselleştirmeler, x eksenleri üzerindeki başlangıç kapasitesini ve y eksenindeki yük faktörünü gösterir.
Daha fazla uzatmadan sonuçlara bir göz atalım. Koyma sonuçlarıyla başlayacağım.
Sonuçları koy
Koleksiyon boyutu: 100. Karma sınırı: 50. Bu, her bir karma kodun iki kez gerçekleşmesi gerektiği ve karma haritada diğer her anahtarın çarpıştığı anlamına gelir.
Bu pek iyi başlamıyor. Koleksiyon boyutunun% 25 üzerinde ve yük faktörü 1 olan ilk kapasite için büyük bir sıcak nokta olduğunu görüyoruz. Sol alt köşe çok iyi performans göstermiyor.
Koleksiyon boyutu: 100. Karma sınırı: 90. On anahtardan biri, yinelenen bir karma koduna sahiptir.
Bu, biraz daha gerçekçi bir senaryodur, mükemmel bir hash fonksiyonuna sahip değil, ancak yine de% 10 aşırı yüke sahip. Hotspot gitti, ancak düşük bir başlangıç kapasitesi ile düşük bir yük faktörü kombinasyonu açıkça işe yaramıyor.
Koleksiyon boyutu: 100. Karma sınırı: 100. Her anahtar kendi benzersiz karma kodu olarak. Yeterli kova varsa çarpışma beklenmez.
Yük faktörü 1 olan 100'lük bir başlangıç kapasitesi iyi görünüyor. Şaşırtıcı bir şekilde, daha düşük bir yük faktörüne sahip daha yüksek bir başlangıç kapasitesi mutlaka iyi değildir.
Koleksiyon boyutu: 1000. Hash limiti: 500. 1000 girişle burada daha ciddileşiyor. Tıpkı ilk testte olduğu gibi, 2'ye 1'lik bir hash aşırı yüklemesi var.
Sol alt köşe hala iyi değil. Ancak, daha düşük başlangıç sayımı / yüksek yük faktörü ve daha yüksek ilk sayım / düşük yük faktörü kombinasyonu arasında bir simetri var gibi görünüyor.
Koleksiyon boyutu: 1000. Karma sınırı: 900. Bu, on karma koddan birinin iki kez gerçekleşeceği anlamına gelir. Çarpışmalarla ilgili makul senaryo.
1'in üzerinde bir yük faktörü ile çok düşük olan bir başlangıç kapasitesinin beklenmedik kombinasyonunda çok komik bir şeyler oluyor ki bu oldukça sezgisel. Aksi takdirde, hala oldukça simetriktir.
Koleksiyon boyutu: 1000. Karma sınırı: 990. Bazı çarpışmalar, ancak yalnızca birkaçı. Bu açıdan oldukça gerçekçi.
Burada güzel bir simetrimiz var. Sol alt köşe hala optimalin altında, ancak kombinasyonlar 1000 başlatma kapasitesi / 1.0 yük faktörüne karşı 1250 başlatma kapasitesi / 0.75 yük faktörü aynı seviyede.
Koleksiyon boyutu: 1000. Karma sınırı: 1000. Yinelenen karma kodlar yok, ancak şimdi 1000 örnek boyutuyla.
Burada söylenecek pek bir şey yok. Daha yüksek bir başlangıç kapasitesinin 0,75'lik bir yük faktörüyle kombinasyonu, 1000 başlangıç kapasitesinin 1'lik bir yük faktörü ile kombinasyonundan biraz daha iyi performans gösteriyor gibi görünüyor.
Koleksiyon boyutu: 100_000. Karma sınırı: 10_000. Pekala, anahtar başına yüz bin 100 karma kod kopyasından oluşan bir örnek boyutuyla şimdi ciddileşiyor.
Eyvah! Sanırım alt spektrumumuzu bulduk. Yükleme faktörü 1 olan toplama boyutunda bir başlangıç kapasitesi gerçekten iyi işliyor, ancak bunun dışında tüm mağazada.
Koleksiyon boyutu: 100_000. Karma sınırı: 90_000. Önceki testten biraz daha gerçekçi, burada hash kodlarında% 10 aşırı yüklenme var.
Sol alt köşe hala istenmiyor. Daha yüksek başlangıç kapasiteleri en iyi sonucu verir.
Koleksiyon boyutu: 100_000. Karma sınırı: 99_000. İyi senaryo, bu. % 1 karma kod aşırı yüklemesine sahip büyük bir koleksiyon.
Tam toplama boyutunu, yükleme faktörü 1 olan başlangıç kapasitesi olarak kullanmak burada kazanır! Yine de biraz daha büyük başlatma kapasiteleri oldukça iyi çalışıyor.
Koleksiyon boyutu: 100_000. Karma sınırı: 100_000. Büyük olan. Mükemmel bir hash işlevine sahip en büyük koleksiyon.
Burada bazı şaşırtıcı şeyler var. 1 yük faktöründe% 50 ek odaya sahip bir ilk kapasite kazanır.
Pekala, koymalar için bu kadar. Şimdi, alırları kontrol edeceğiz. Unutmayın, aşağıdaki haritaların tümü en iyi / en kötü alma zamanlarına bağlıdır, koyma süreleri artık dikkate alınmaz.
Sonuç almak
Koleksiyon boyutu: 100. Karma sınırı: 50. Bu, her bir karma kodun iki kez gerçekleşmesi gerektiği ve diğer anahtarların karma haritada çarpışmasının beklendiği anlamına gelir.
Eh ... Ne?
Koleksiyon boyutu: 100. Karma sınırı: 90. On anahtardan biri, yinelenen bir karma koduna sahiptir.
Whoa Nelly! Bu, soruyu soranın sorusuyla ilişkilendirilebilecek en olası senaryodur ve görünüşe göre 1 yük faktörü ile 100'lük bir başlangıç kapasitesi buradaki en kötü şeylerden biridir! Yemin ederim bunu kandırmadım.
Koleksiyon boyutu: 100. Karma sınırı: 100. Her anahtar kendi benzersiz karma kodu olarak. Çarpışma beklenmiyor.
Bu biraz daha huzurlu görünüyor. Genelde genel olarak aynı sonuçlar.
Koleksiyon boyutu: 1000. Hash limiti: 500. İlk testte olduğu gibi, 2'ye 1'lik bir hash aşırı yüklemesi var, ancak şimdi çok daha fazla giriş var.
Görünüşe göre herhangi bir ayar burada iyi bir sonuç verecek.
Koleksiyon boyutu: 1000. Karma sınırı: 900. Bu, on karma koddan birinin iki kez gerçekleşeceği anlamına gelir. Çarpışmalarla ilgili makul senaryo.
Ve tıpkı bu düzeneğin koymalarında olduğu gibi, garip bir noktada bir anormallik alıyoruz.
Koleksiyon boyutu: 1000. Karma sınırı: 990. Bazı çarpışmalar, ancak yalnızca birkaçı. Bu açıdan oldukça gerçekçi.
Her yerde iyi performans, yüksek başlangıç kapasitesi ile düşük yük faktörünün birleşiminden tasarruf edin. İki hash harita yeniden boyutlandırması beklenebileceğinden, bunu koymalar için bekliyorum. Ama neden paralı?
Koleksiyon boyutu: 1000. Karma sınırı: 1000. Yinelenen karma kodlar yok, ancak şimdi 1000 örnek boyutuyla.
Tamamen olağanüstü bir görselleştirme. Bu ne olursa olsun işe yarıyor gibi görünüyor.
Koleksiyon boyutu: 100_000. Karma sınırı: 10_000. Bir sürü karma kod çakışmasıyla tekrar 100K'ya giriyoruz.
Kötü noktalar çok lokalize olmasına rağmen hoş görünmüyor. Buradaki performans, büyük ölçüde ayarlar arasındaki belirli bir sinerjiye bağlı görünüyor.
Koleksiyon boyutu: 100_000. Karma sınırı: 90_000. Önceki testten biraz daha gerçekçi, burada hash kodlarında% 10 aşırı yüklenme var.
Pek çok varyans, ancak gözlerinizi kısarsanız sağ üst köşeyi işaret eden bir ok görebilirsiniz.
Koleksiyon boyutu: 100_000. Karma sınırı: 99_000. İyi senaryo, bu. % 1 karma kod aşırı yüklemesine sahip büyük bir koleksiyon.
Çok kaotik. Burada çok fazla yapı bulmak zor.
Koleksiyon boyutu: 100_000. Karma sınırı: 100_000. Büyük olan. Mükemmel bir hash işlevine sahip en büyük koleksiyon.
Bunun Atari grafiklerine benzemeye başladığını düşünen başka kimse var mı? Bu, tam olarak toplama boyutunun -% 25 veya +% 50'lik bir başlangıç kapasitesini desteklemektedir.
Pekala, şimdi sonuç çıkarma zamanı ...
- Yerleştirme süreleriyle ilgili olarak: beklenen harita girişi sayısından daha düşük başlangıç kapasitelerinden kaçınmak isteyeceksiniz. Önceden kesin bir sayı biliniyorsa, bu sayı veya bunun biraz üzerinde bir şey en iyi sonucu verir. Yüksek yük faktörleri, önceki karma harita yeniden boyutlandırmaları nedeniyle daha düşük başlangıç kapasitelerini dengeleyebilir. Daha yüksek başlangıç kapasiteleri için, o kadar önemli görünmüyorlar.
- Get times ile ilgili olarak: sonuçlar burada biraz kaotik. Sonuçlandırılacak pek bir şey yok. Karma kod çakışması, başlangıç kapasitesi ve yük faktörü arasındaki ince oranlara çok güveniyor gibi görünüyor, bazı sözde kötü kurulumlar iyi performans gösteriyor ve iyi kurulumlar çok kötü çalışıyor.
- Java performansıyla ilgili varsayımlar söz konusu olduğunda, görünüşe göre saçma sapan şeylerim var. Gerçek şu ki, ayarlarınızı uygulamasına mükemmel bir şekilde ayarlamadığınız sürece
HashMap
, sonuçlar her yerde olacaktır. Bundan çıkarılacak bir şey varsa, o da 16 olan varsayılan başlangıç boyutunun en küçük haritalardan başka her şey için biraz aptalca olmasıdır, bu nedenle hangi boyut sırası hakkında herhangi bir fikriniz varsa başlangıç boyutunu ayarlayan bir kurucu kullanın. Olacak.
- Burada nanosaniye cinsinden ölçüyoruz. Makinemde 10 vuruş başına en iyi ortalama süre 1179 ns ve en kötü 5105 ns idi. 10 çekim başına en iyi ortalama süre 547 ns ve en kötü 3484 ns idi. Bu 6 faktör farkı olabilir, ancak bir milisaniyeden daha az konuşuyoruz. Orijinal posterin aklından çok daha büyük koleksiyonlar üzerine.
İşte bu. Umarım kodumun, burada yayınladığım her şeyi geçersiz kılan korkunç bir denetimi yoktur. Bu eğlenceliydi ve sonunda küçük optimizasyonlardan çok fazla farklılık beklemektense işini yapmak için Java'ya güvenebileceğinizi öğrendim. Bu, bazı şeylerden kaçınılmaması gerektiği anlamına gelmez, ancak o zaman çoğunlukla döngüler için uzun Dizeler oluşturmaktan, yanlış veri yapılarını kullanmaktan ve O (n ^ 3) algoritmaları yapmaktan bahsediyoruz.