ठीक है, इस चीज़ को आराम करने के लिए, मैंने कुछ परिदृश्यों को चलाने के लिए एक परीक्षण ऐप बनाया है और परिणामों के कुछ दृश्य प्राप्त किए हैं। यहाँ परीक्षण कैसे किए जाते हैं:
- विभिन्न संग्रह आकारों की एक संख्या की कोशिश की गई है: एक सौ, एक हजार और एक सौ हजार प्रविष्टियां।
- उपयोग की जाने वाली चाबियाँ एक वर्ग के उदाहरण हैं जो एक आईडी द्वारा विशिष्ट रूप से पहचानी जाती हैं। प्रत्येक परीक्षण अद्वितीय कुंजियों का उपयोग करता है, आईडी के रूप में पूर्णांक बढ़ाता है।
equals
विधि केवल, आईडी का उपयोग करता है ताकि कोई कुंजी मानचित्रण एक दूसरे से अधिलेखित कर देता है।
- कुंजी में एक हैश कोड होता है जिसमें कुछ पूर्व निर्धारित संख्या के मुकाबले उनकी आईडी के शेष मॉड्यूल होते हैं। हम उस नंबर को हैश लिमिट कहेंगे । इसने मुझे हैश टक्करों की संख्या को नियंत्रित करने की अनुमति दी जो अपेक्षित होगी। उदाहरण के लिए, यदि हमारे संग्रह का आकार 100 है, तो हमारे पास 0 से 99 तक की आईडी वाली चाबियां होंगी। यदि हैश सीमा 100 है, तो प्रत्येक कुंजी में एक अद्वितीय हैश कोड होगा। यदि हैश की सीमा 50 है, तो कुंजी 0 में कुंजी 50 के समान हैश कोड होगा, 1 में समान हैश कोड होगा जैसे 51 आदि। दूसरे शब्दों में, प्रति कुंजी हैश टक्कर की अपेक्षित संख्या हैश द्वारा विभाजित संग्रह आकार है। सीमा।
- संग्रह आकार और हैश सीमा के प्रत्येक संयोजन के लिए, मैंने अलग-अलग सेटिंग्स के साथ आरम्भ किए गए हैश मानचित्रों का उपयोग करके परीक्षण चलाया है। ये सेटिंग्स लोड फैक्टर हैं, और एक प्रारंभिक क्षमता जिसे संग्रह सेटिंग के कारक के रूप में व्यक्त किया गया है। उदाहरण के लिए, 100 के संग्रह के आकार के साथ एक परीक्षण और 1.25 की प्रारंभिक क्षमता कारक 125 की प्रारंभिक क्षमता के साथ एक हैश मानचित्र को प्रारंभ करेगा।
- प्रत्येक कुंजी का मान केवल एक नया है
Object
।
- प्रत्येक परीक्षा परिणाम एक परिणाम वर्ग के उदाहरण में समझाया गया है। सभी परीक्षणों के अंत में, परिणामों को सबसे खराब समग्र प्रदर्शन से सर्वश्रेष्ठ करने का आदेश दिया जाता है।
- पुट्स और हो जाता है के लिए औसत समय की गणना 10 पुट / मिलती है।
- जेआईटी संकलन प्रभाव को खत्म करने के लिए सभी परीक्षण संयोजन एक बार चलाए जाते हैं। उसके बाद, परीक्षण वास्तविक परिणामों के लिए चलाए जाते हैं।
यहाँ वर्ग है:
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.");
}
}
}
इसे चलाने में कुछ समय लग सकता है। परिणाम बाहर मानक पर मुद्रित कर रहे हैं। आप देख सकते हैं कि मैंने एक पंक्ति में टिप्पणी की है। वह लाइन एक विज़ुअलाइज़र को कॉल करती है जो फ़ाइलों को png करने के लिए परिणामों के दृश्य प्रतिनिधित्व को आउटपुट करता है। इसके लिए वर्ग नीचे दिया गया है। यदि आप इसे चलाना चाहते हैं, तो ऊपर दिए गए कोड में उपयुक्त लाइन को अनइंस्टॉल करें। सावधान रहें: विज़ुअलाइज़र क्लास मानती है कि आप विंडोज पर चल रहे हैं और C: \ temp में फ़ोल्डर्स और फाइल्स बनाएंगे। दूसरे प्लेटफ़ॉर्म पर चलने पर, इसे समायोजित करें।
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);
}
}
विज़ुअलाइज़्ड आउटपुट निम्नानुसार है:
- टेस्ट को पहले कलेक्शन साइज़ से, फिर हैश लिमिट से विभाजित किया जाता है।
- प्रत्येक परीक्षण के लिए, औसत पुट टाइम (प्रति 10 पुट) के बारे में एक आउटपुट छवि है और औसत समय मिलता है (प्रति 10 मिलता है)। चित्र दो-आयामी "हीट मैप्स" हैं जो प्रारंभिक क्षमता और लोड कारक के संयोजन के अनुसार एक रंग दिखाते हैं।
- छवियों में रंग सबसे अच्छे से खराब परिणाम के लिए सामान्यीकृत पैमाने पर औसत समय पर आधारित हैं, संतृप्त हरे से संतृप्त लाल तक। दूसरे शब्दों में, सबसे अच्छा समय पूरी तरह से हरा होगा, जबकि सबसे खराब समय पूरी तरह से लाल होगा। दो अलग-अलग समय माप में कभी भी एक जैसा रंग नहीं होना चाहिए।
- रंग मानचित्रों को पुट और हो जाता है के लिए अलग से गणना की जाती है, लेकिन उनकी संबंधित श्रेणियों के लिए सभी परीक्षण शामिल हैं।
- विज़ुअलाइज़ेशन उनके x अक्ष पर प्रारंभिक क्षमता और y अक्ष पर लोड कारक दिखाते हैं।
आगे की हलचल के बिना, आइए परिणामों पर एक नज़र डालें। मैं पुट के लिए परिणामों के साथ शुरू करूँगा।
परिणाम डालें
संग्रह का आकार: 100. हैश सीमा: 50. इसका मतलब है कि प्रत्येक हैश कोड दो बार और हर दूसरे को हैश मानचित्र में टकरा जाना चाहिए।
खैर, यह बहुत अच्छा शुरू नहीं करता है। हम देखते हैं कि संग्रह आकार के ऊपर 25% की प्रारंभिक क्षमता के लिए एक बड़ा हॉटस्पॉट है, जिसमें 1 का लोड फैक्टर है। निचले बाएं कोने का प्रदर्शन बहुत अच्छा नहीं है।
संग्रह का आकार: 100. हैश सीमा: 90. दस कुंजियों में से एक में डुप्लिकेट हैश कोड होता है।
यह एक और अधिक यथार्थवादी परिदृश्य है, जिसमें एक पूर्ण हैश फ़ंक्शन नहीं है लेकिन फिर भी 10% अधिभार है। हॉटस्पॉट चला गया है, लेकिन कम लोड फैक्टर के साथ कम प्रारंभिक क्षमता का संयोजन स्पष्ट रूप से काम नहीं करता है।
संग्रह का आकार: 100. हैश सीमा: 100. प्रत्येक कुंजी का अपना विशिष्ट हैश कोड होता है। यदि पर्याप्त बाल्टियाँ हैं तो किसी भी टक्कर की उम्मीद नहीं है।
1 के लोड फैक्टर के साथ 100 की प्रारंभिक क्षमता ठीक लगती है। हैरानी की बात है, कम लोड कारक के साथ एक उच्च प्रारंभिक क्षमता जरूरी अच्छा नहीं है।
संग्रह का आकार: 1000. हैश सीमा: 500. यह 1000 से अधिक प्रविष्टियों के साथ यहां और अधिक गंभीर हो रहा है। पहले टेस्ट की तरह ही, इसमें 2 से 1 का हैश ओवरलोड है।
निचले बाएं कोने में अभी भी अच्छा नहीं चल रहा है। लेकिन कम प्रारंभिक गिनती / उच्च लोड कारक और उच्च प्रारंभिक गणना / कम लोड कारक के कॉम्बो के बीच एक समरूपता प्रतीत होती है।
संग्रह का आकार: 1000. हैश सीमा: 900। इसका मतलब है कि दस हैश कोड में एक दो बार होगा। टकराव के संबंध में उचित परिदृश्य।
वहाँ एक बहुत ही अजीब बात है एक प्रारंभिक क्षमता की संभावना कॉम्बो के साथ चल रहा है कि 1 से ऊपर एक लोड फैक्टर के साथ बहुत कम है, जो प्रति-सहज है। अन्यथा, अभी भी काफी सममित है।
संग्रह का आकार: 1000. हैश सीमा: 990. कुछ टकराव, लेकिन केवल कुछ। इस संबंध में काफी यथार्थवादी।
हमें यहां एक अच्छी समरूपता मिली है। निचले बाएं कोने अभी भी उप-इष्टतम हैं, लेकिन कॉम्बो 1000 इनिट क्षमता / 1.0 लोड कारक बनाम 1250 इनिट क्षमता / 0.75 लोड फैक्टर समान स्तर पर हैं।
संग्रह का आकार: 1000। हैश सीमा: 1000। कोई डुप्लिकेट हैश कोड नहीं है, लेकिन अब 1000 का नमूना आकार है।
यहां ज्यादा कुछ नहीं कहा जा सकता है। 0.75 के लोड फैक्टर के साथ उच्च प्रारंभिक क्षमता का संयोजन 1 के लोड फैक्टर के साथ 1000 प्रारंभिक क्षमता के संयोजन को थोड़ा बेहतर बनाता है।
संग्रह का आकार: 100_000। हैश लिमिट: 10_000। ठीक है, यह अब गंभीर हो रहा है, प्रति सैकड़ा एक हजार और 100 हैश कोड के सैंपल साइज के साथ।
ओह! मुझे लगता है कि हमने अपना निचला स्पेक्ट्रम पाया। 1 के लोड फैक्टर के साथ वास्तव में संग्रह आकार की एक init क्षमता वास्तव में यहाँ अच्छी तरह से कर रही है, लेकिन इसके अलावा यह पूरी दुकान पर है।
संग्रह का आकार: 100_000। हैश लिमिट: 90_000। पिछले परीक्षण की तुलना में थोड़ा अधिक यथार्थवादी, यहाँ हमें हैश कोड में 10% अधिभार मिला है।
निचले बाएँ कोने अभी भी अवांछनीय है। उच्च प्रारंभिक क्षमता सबसे अच्छा काम करती है।
संग्रह का आकार: 100_000। हैश लिमिट: 99_000। अच्छा परिदृश्य, यह। 1% हैश कोड अधिभार के साथ एक बड़ा संग्रह।
1 संग्रह के भार कारक के साथ init क्षमता के रूप में सटीक संग्रह आकार का उपयोग करके यहाँ से जीतता है! हालांकि थोड़ा बड़ा इनिट कैपेसिटी काफी अच्छा काम करता है।
संग्रह का आकार: 100_000। हैश लिमिट: 100_000। बड़ा वाला। एक सही हैश फ़ंक्शन के साथ सबसे बड़ा संग्रह।
कुछ आश्चर्यजनक सामान यहाँ। 1 जीत के लोड कारक पर 50% अतिरिक्त कमरे के साथ एक प्रारंभिक क्षमता।
ठीक है, यह पुट के लिए है। अब, हम मिल की जाँच करेंगे। याद रखें, नीचे दिए गए नक्शे सभी सबसे अच्छे / सबसे खराब समय के सापेक्ष हैं, पुट टाइम को ध्यान में नहीं रखा जाता है।
परिणाम प्राप्त करें
संग्रह का आकार: 100. हैश सीमा: 50. इसका मतलब है कि प्रत्येक हैश कोड दो बार होना चाहिए और हर दूसरे कुंजी को हैश मानचित्र में टकराने की उम्मीद थी।
एह ... क्या?
संग्रह का आकार: 100. हैश सीमा: 90. दस कुंजियों में से एक में डुप्लिकेट हैश कोड होता है।
वाह नेली! यह पूछने वाले के प्रश्न के साथ सहसंबंधित करने के लिए सबसे अधिक संभावना परिदृश्य है, और जाहिर तौर पर 1 के लोड फैक्टर के साथ 100 की प्रारंभिक क्षमता यहां सबसे खराब चीजों में से एक है! मैं कसम खाता हूँ कि मैं यह नकली नहीं था।
संग्रह का आकार: 100. हैश सीमा: 100. प्रत्येक कुंजी का अपना विशिष्ट हैश कोड होता है। कोई टक्कर की उम्मीद नहीं है।
यह थोड़ा और शांतिपूर्ण लगता है। ज्यादातर बोर्ड भर में एक ही परिणाम।
संग्रह का आकार: 1000. हैश सीमा: 500। पहले टेस्ट की तरह, इसमें 2 से 1 का हैश ओवरलोड है, लेकिन अब बहुत अधिक प्रविष्टियों के साथ।
ऐसा लगता है कि किसी भी सेटिंग से यहां अच्छा परिणाम मिलेगा।
संग्रह का आकार: 1000. हैश सीमा: 900। इसका मतलब है कि दस हैश कोड में एक दो बार होगा। टकराव के संबंध में उचित परिदृश्य।
और बस इस सेटअप के लिए पुट्स के साथ, हमें एक अजीब जगह में एक विसंगति मिलती है।
संग्रह का आकार: 1000. हैश सीमा: 990. कुछ टकराव, लेकिन केवल कुछ। इस संबंध में काफी यथार्थवादी।
हर जगह निर्णय लेना, कम भार कारक के साथ उच्च प्रारंभिक क्षमता के संयोजन के लिए बचत करना। मुझे उम्मीद है कि पुट के लिए, दो हैश मानचित्र के आकार की उम्मीद की जा सकती है। पर क्यों मिलता है?
संग्रह का आकार: 1000। हैश सीमा: 1000। कोई डुप्लिकेट हैश कोड नहीं है, लेकिन अब 1000 का नमूना आकार है।
एक पूर्ण अलौकिक दृश्य। यह काम करने लगता है चाहे कुछ भी हो।
संग्रह का आकार: 100_000। हैश लिमिट: 10_000। फिर से 100K में जा रहा है, पूरे हैश कोड ओवरलैप के साथ।
यह सुंदर नहीं दिखता है, हालांकि खराब स्थान बहुत स्थानीय हैं। यहाँ प्रदर्शन सेटिंग्स के बीच एक निश्चित तालमेल पर काफी हद तक निर्भर करता है।
संग्रह का आकार: 100_000। हैश लिमिट: 90_000। पिछले परीक्षण की तुलना में थोड़ा अधिक यथार्थवादी, यहाँ हमें हैश कोड में 10% अधिभार मिला है।
बहुत अधिक विचरण, हालांकि यदि आप स्क्विंट करते हैं तो आप ऊपरी दाहिने कोने की ओर इशारा करते हुए एक तीर देख सकते हैं।
संग्रह का आकार: 100_000। हैश लिमिट: 99_000। अच्छा परिदृश्य, यह। 1% हैश कोड अधिभार के साथ एक बड़ा संग्रह।
बहुत अराजक। यहां बहुत संरचना खोजना मुश्किल है।
संग्रह का आकार: 100_000। हैश लिमिट: 100_000। बड़ा वाला। एक सही हैश फ़ंक्शन के साथ सबसे बड़ा संग्रह।
किसी और को लगता है कि यह अटारी ग्राफिक्स जैसा दिखने लगा है? यह वास्तव में संग्रह आकार, -25% या + 50% की प्रारंभिक क्षमता का पक्ष लेता है।
ठीक है, अब निष्कर्ष के लिए समय है ...
- पुट टाइम के बारे में: आप प्रारंभिक क्षमताओं से बचना चाहते हैं जो मानचित्र प्रविष्टियों की अपेक्षित संख्या से कम हों। यदि एक सटीक संख्या पहले से ज्ञात है, तो वह संख्या या थोड़ा ऊपर यह सबसे अच्छा काम करता है। उच्च लोड कारक पहले के हैश मानचित्र के आकार के कारण कम प्रारंभिक क्षमताओं को ऑफसेट कर सकते हैं। उच्च प्रारंभिक क्षमताओं के लिए, वे उतना मायने नहीं रखते हैं।
- समय के बारे में: परिणाम यहाँ थोड़ा अव्यवस्थित हैं। निष्कर्ष निकालने के लिए बहुत कुछ नहीं है। यह हैश कोड ओवरलैप, प्रारंभिक क्षमता और लोड फैक्टर के बीच सूक्ष्म अनुपात पर बहुत अधिक भरोसा करता है, कुछ माना जाता है कि खराब सेटअप अच्छा प्रदर्शन कर रहे हैं और अच्छे सेटअप शानदार प्रदर्शन कर रहे हैं।
- मैं जाहिरा तौर पर बकवास से भरा हुआ हूं जब यह जावा प्रदर्शन के बारे में मान्यताओं की बात आती है। सच्चाई यह है कि जब तक आप अपनी सेटिंग्स को पूरी तरह से लागू करने के लिए तैयार नहीं होते हैं, तब तक
HashMap
परिणाम सभी जगह होने वाले हैं। अगर इसमें से एक चीज़ निकालनी है, तो यह है कि 16 का डिफ़ॉल्ट प्रारंभिक आकार किसी भी चीज़ के लिए थोड़ा गूंगा है, लेकिन सबसे छोटे नक्शे हैं, इसलिए एक निर्माता का उपयोग करें जो प्रारंभिक आकार सेट करता है यदि आपके पास आकार के क्रम के बारे में किसी प्रकार का विचार है यह होने वाला है।
- हम यहाँ नैनोसेकंड में माप रहे हैं। प्रति 10 मिनट में सबसे अच्छा औसत समय 1179 एनएस था और मेरी मशीन पर सबसे खराब 5105 एनएस। प्रति 10 में सबसे अच्छा औसत समय 547 एनएस और सबसे खराब 3484 एनएस था। यह एक कारक 6 अंतर हो सकता है, लेकिन हम एक मिलीसेकंड से कम बात कर रहे हैं। उन संग्रहों पर जो मूल पोस्टर की तुलना में बहुत बड़े हैं, जो उनके दिमाग में थे।
हां इसी तरह। मुझे उम्मीद है कि मेरे कोड में कुछ भयावह ओवरसाइट नहीं हैं जो मेरे द्वारा पोस्ट की गई हर चीज को अमान्य करते हैं। यह मज़ेदार रहा है, और मैंने सीखा है कि अंत में आप छोटे कामों से बहुत अंतर की अपेक्षा जावा पर अपना काम करने के लिए भरोसा कर सकते हैं। यह कहना नहीं है कि कुछ सामान से बचा नहीं जाना चाहिए, लेकिन फिर हम ज्यादातर लूप्स के लिए लंबे स्ट्रिंग्स का निर्माण करने की बात कर रहे हैं, गलत डेटास्ट्रक्चर का उपयोग कर रहे हैं और ओ (एन ^ 3) एल्गोरिदम बना रहे हैं।