Được rồi, để tạm dừng việc này, tôi đã tạo một ứng dụng thử nghiệm để chạy một vài tình huống và nhận được một số hình dung về kết quả. Đây là cách các bài kiểm tra được thực hiện:
- Một số kích thước bộ sưu tập khác nhau đã được thử: một trăm, một nghìn và một trăm nghìn mục nhập.
- Các khóa được sử dụng là các phiên bản của một lớp được nhận dạng duy nhất bởi một ID. Mỗi bài kiểm tra sử dụng các khóa duy nhất, với các số nguyên tăng dần làm ID. Các
equals
phương pháp duy nhất sử dụng ID, vì vậy không có bản đồ chủ chốt ghi đè nhau.
- Các khóa nhận được một mã băm bao gồm phần còn lại của mô-đun của ID của chúng dựa trên một số đặt trước. Chúng tôi sẽ gọi số đó là giới hạn băm . Điều này cho phép tôi kiểm soát số lượng xung đột băm sẽ được mong đợi. Ví dụ: nếu kích thước bộ sưu tập của chúng tôi là 100, chúng tôi sẽ có các khóa có ID từ 0 đến 99. Nếu giới hạn băm là 100, mọi khóa sẽ có một mã băm duy nhất. Nếu giới hạn băm là 50, khóa 0 sẽ có cùng mã băm với khóa 50, 1 sẽ có cùng mã băm là 51, v.v. Nói cách khác, số lần va chạm băm dự kiến trên mỗi khóa là kích thước tập hợp chia cho hàm băm giới hạn.
- Đối với mỗi sự kết hợp giữa kích thước bộ sưu tập và giới hạn băm, tôi đã chạy thử nghiệm bằng cách sử dụng bản đồ băm được khởi tạo với các cài đặt khác nhau. Các cài đặt này là hệ số tải và công suất ban đầu được biểu thị như một hệ số của cài đặt thu. Ví dụ: một bài kiểm tra với kích thước tập hợp là 100 và hệ số dung lượng ban đầu là 1,25 sẽ khởi tạo một bản đồ băm với dung lượng ban đầu là 125.
- Giá trị cho mỗi khóa chỉ đơn giản là một khóa mới
Object
.
- Mỗi kết quả kiểm tra được đóng gói trong một thể hiện của lớp Kết quả. Vào cuối tất cả các bài kiểm tra, kết quả được sắp xếp từ hiệu suất tổng thể kém nhất đến tốt nhất.
- Thời gian trung bình để đặt và nhận được tính trên 10 lần đặt / được.
- Tất cả các kết hợp thử nghiệm được chạy một lần để loại bỏ ảnh hưởng của quá trình biên dịch JIT. Sau đó, các bài kiểm tra được chạy để có kết quả thực tế.
Đây là lớp học:
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.");
}
}
}
Việc chạy này có thể mất một lúc. Kết quả được in ra tiêu chuẩn. Bạn có thể nhận thấy tôi đã nhận xét ra một dòng. Dòng đó gọi một trình hiển thị hiển thị các biểu diễn trực quan của kết quả thành tệp png. Lớp cho điều này được đưa ra dưới đây. Nếu bạn muốn chạy nó, hãy bỏ ghi chú dòng thích hợp trong đoạn mã trên. Được cảnh báo: lớp trình hiển thị giả định bạn đang chạy trên Windows và sẽ tạo các thư mục và tệp trong C: \ temp. Khi chạy trên nền tảng khác, hãy điều chỉnh điều này.
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);
}
}
Đầu ra được hiển thị như sau:
- Trước tiên, các thử nghiệm được chia theo kích thước tập hợp, sau đó là giới hạn băm.
- Đối với mỗi bài kiểm tra, có một hình ảnh đầu ra liên quan đến thời gian đặt trung bình (mỗi 10 lần đặt) và thời gian nhận trung bình (trên 10 lần đặt). Các hình ảnh là "bản đồ nhiệt" hai chiều hiển thị một màu cho mỗi sự kết hợp của công suất ban đầu và hệ số tải.
- Màu sắc trong hình ảnh dựa trên thời gian trung bình trên thang chuẩn hóa từ kết quả tốt nhất đến xấu nhất, từ màu xanh lá cây bão hòa đến màu đỏ bão hòa. Nói cách khác, thời điểm tốt nhất sẽ có màu xanh hoàn toàn, trong khi thời điểm xấu nhất sẽ có màu đỏ hoàn toàn. Hai phép đo thời gian khác nhau không bao giờ được có cùng màu.
- Các bản đồ màu được tính toán riêng biệt cho lượt đặt và lượt nhận, nhưng bao gồm tất cả các bài kiểm tra cho các danh mục tương ứng của chúng.
- Hình ảnh trực quan cho thấy công suất ban đầu trên trục x và hệ số tải trên trục y.
Không cần thêm lời khuyên, hãy cùng xem kết quả. Tôi sẽ bắt đầu với kết quả đặt.
Đặt kết quả
Kích thước bộ sưu tập: 100. Giới hạn băm: 50. Điều này có nghĩa là mỗi mã băm phải xảy ra hai lần và mọi khóa khác xung đột trong bản đồ băm.

Chà, điều đó không khởi đầu tốt lắm. Chúng tôi thấy rằng có một điểm phát sóng lớn cho công suất ban đầu cao hơn 25% so với kích thước bộ sưu tập, với hệ số tải là 1. Góc dưới bên trái hoạt động không quá tốt.
Kích thước bộ sưu tập: 100. Giới hạn băm: 90. Một trong mười khóa có mã băm trùng lặp.

Đây là một kịch bản thực tế hơn một chút, không có một hàm băm hoàn hảo nhưng vẫn quá tải 10%. Điểm phát sóng đã biến mất, nhưng sự kết hợp giữa dung lượng ban đầu thấp với hệ số tải thấp rõ ràng là không hoạt động.
Kích thước bộ sưu tập: 100. Giới hạn băm: 100. Mỗi khóa như một mã băm duy nhất của riêng nó. Không có va chạm dự kiến nếu có đủ xô.

Công suất ban đầu là 100 với hệ số tải là 1 có vẻ ổn. Đáng ngạc nhiên, công suất ban đầu cao hơn với hệ số tải thấp hơn không nhất thiết là tốt.
Kích thước bộ sưu tập: 1000. Giới hạn băm: 500. Ở đây ngày càng nghiêm trọng hơn, với 1000 mục nhập. Giống như trong thử nghiệm đầu tiên, có quá tải băm từ 2 đến 1.

Góc dưới bên trái vẫn không hoạt động tốt. Nhưng dường như có sự đối xứng giữa sự kết hợp của số lượng ban đầu thấp hơn / hệ số tải cao và số lượng ban đầu cao hơn / hệ số tải thấp.
Kích thước tập hợp: 1000. Giới hạn băm: 900. Điều này có nghĩa là một trong mười mã băm sẽ xảy ra hai lần. Kịch bản hợp lý liên quan đến va chạm.

Có điều gì đó rất buồn cười đang xảy ra với sự kết hợp không chắc chắn của công suất ban đầu quá thấp với hệ số tải trên 1, điều này khá phản trực quan. Nếu không, vẫn khá đối xứng.
Kích thước bộ sưu tập: 1000. Giới hạn băm: 990. Một số va chạm, nhưng chỉ một số ít. Khá thực tế về mặt này.

Chúng ta có một sự đối xứng đẹp ở đây. Góc dưới bên trái vẫn chưa tối ưu, nhưng kết hợp công suất 1000 init / hệ số tải 1,0 so với công suất 1250 init / hệ số tải 0,75 là ở cùng một mức.
Kích thước bộ sưu tập: 1000. Giới hạn băm: 1000. Không có mã băm trùng lặp, nhưng hiện có kích thước mẫu là 1000.

Không có nhiều điều để nói ở đây. Sự kết hợp của công suất ban đầu cao hơn với hệ số tải 0,75 có vẻ tốt hơn một chút so với sự kết hợp của công suất ban đầu 1000 với hệ số tải là 1.
Kích thước bộ sưu tập: 100_000. Giới hạn băm: 10_000. Được rồi, nó đang trở nên nghiêm trọng với kích thước mẫu là một trăm nghìn và 100 bản sao mã băm trên mỗi khóa.

Rất tiếc! Tôi nghĩ rằng chúng tôi đã tìm thấy phổ thấp hơn của mình. Công suất init có kích thước chính xác với kích thước bộ sưu tập với hệ số tải là 1 đang hoạt động rất tốt ở đây, nhưng khác với điều đó là tất cả các cửa hàng.
Kích thước bộ sưu tập: 100_000. Giới hạn băm: 90_000. Thực tế hơn một chút so với thử nghiệm trước, ở đây chúng tôi có quá tải 10% trong mã băm.

Góc dưới bên trái vẫn không mong muốn. Công suất ban đầu cao hơn hoạt động tốt nhất.
Kích thước bộ sưu tập: 100_000. Giới hạn băm: 99_000. Kịch bản tốt, điều này. Một bộ sưu tập lớn với quá tải mã băm 1%.

Sử dụng kích thước tập hợp chính xác làm dung lượng init với hệ số tải là 1 sẽ thắng ở đây! Tuy nhiên, dung lượng init lớn hơn một chút hoạt động khá tốt.
Kích thước bộ sưu tập: 100_000. Giới hạn băm: 100_000. Cái lớn. Bộ sưu tập lớn nhất với hàm băm hoàn hảo.

Một số thứ đáng ngạc nhiên ở đây. Công suất ban đầu có thêm 50% phòng với hệ số tải là 1 sẽ thắng.
Được rồi, thế là xong. Bây giờ, chúng tôi sẽ kiểm tra những thứ được. Hãy nhớ rằng, tất cả các bản đồ dưới đây đều liên quan đến thời gian tốt nhất / xấu nhất, thời gian đặt không còn được tính đến nữa.
Nhận kết quả
Kích thước bộ sưu tập: 100. Giới hạn băm: 50. Điều này có nghĩa là mỗi mã băm phải xảy ra hai lần và mọi khóa khác dự kiến sẽ xung đột trong bản đồ băm.

Hả ... Gì cơ?
Kích thước bộ sưu tập: 100. Giới hạn băm: 90. Một trong mười khóa có mã băm trùng lặp.

Whoa Nelly! Đây là tình huống có khả năng xảy ra nhất tương quan với câu hỏi của người hỏi, và rõ ràng công suất ban đầu là 100 với hệ số tải là 1 là một trong những điều tồi tệ nhất ở đây! Tôi thề rằng tôi không giả mạo điều này.
Kích thước bộ sưu tập: 100. Giới hạn băm: 100. Mỗi khóa như một mã băm duy nhất của riêng nó. Không có va chạm mong đợi.

Điều này trông yên bình hơn một chút. Hầu hết các kết quả giống nhau trên toàn bộ bảng.
Kích thước bộ sưu tập: 1000. Giới hạn băm: 500. Giống như trong thử nghiệm đầu tiên, có quá tải băm là 2 đến 1, nhưng bây giờ với nhiều mục nhập hơn.

Có vẻ như bất kỳ cài đặt nào cũng sẽ mang lại kết quả tốt ở đây.
Kích thước tập hợp: 1000. Giới hạn băm: 900. Điều này có nghĩa là một trong mười mã băm sẽ xảy ra hai lần. Kịch bản hợp lý liên quan đến va chạm.

Và cũng giống như những lần đặt cho thiết lập này, chúng tôi nhận thấy sự bất thường ở một điểm kỳ lạ.
Kích thước bộ sưu tập: 1000. Giới hạn băm: 990. Một số va chạm, nhưng chỉ một số ít. Khá thực tế về mặt này.

Hiệu suất cao ở mọi nơi, tiết kiệm cho sự kết hợp của công suất ban đầu cao với hệ số tải thấp. Tôi mong đợi điều này cho các lần đặt, vì có thể mong đợi hai lần thay đổi kích thước bản đồ băm. Nhưng tại sao lại được?
Kích thước bộ sưu tập: 1000. Giới hạn băm: 1000. Không có mã băm trùng lặp, nhưng hiện có kích thước mẫu là 1000.

Một hình ảnh hoàn toàn không có hình ảnh. Điều này dường như hoạt động không có vấn đề gì.
Kích thước bộ sưu tập: 100_000. Giới hạn băm: 10_000. Đi vào 100K một lần nữa, với rất nhiều mã băm trùng lặp.

Nó trông không đẹp, mặc dù các điểm xấu được bản địa hóa rất nhiều. Hiệu suất ở đây dường như phụ thuộc phần lớn vào sự phối hợp nhất định giữa các cài đặt.
Kích thước bộ sưu tập: 100_000. Giới hạn băm: 90_000. Thực tế hơn một chút so với thử nghiệm trước, ở đây chúng tôi có quá tải 10% trong mã băm.

Nhiều phương sai, mặc dù nếu bạn liếc mắt, bạn có thể thấy một mũi tên chỉ vào góc trên bên phải.
Kích thước bộ sưu tập: 100_000. Giới hạn băm: 99_000. Kịch bản tốt, điều này. Một bộ sưu tập lớn với quá tải mã băm 1%.

Hỗn loạn lắm. Thật khó để tìm thấy nhiều cấu trúc ở đây.
Kích thước bộ sưu tập: 100_000. Giới hạn băm: 100_000. Cái lớn. Bộ sưu tập lớn nhất với hàm băm hoàn hảo.

Có ai khác nghĩ rằng cái này bắt đầu giống đồ họa Atari không? Điều này dường như ưu tiên dung lượng ban đầu bằng chính xác kích thước bộ sưu tập, -25% hoặc + 50%.
Được rồi, bây giờ là lúc kết luận ...
- Về thời gian thực hiện: bạn sẽ muốn tránh dung lượng ban đầu thấp hơn số lượng mục nhập bản đồ dự kiến. Nếu một con số chính xác được biết trước, con số đó hoặc thứ gì đó cao hơn một chút dường như hoạt động tốt nhất. Hệ số tải cao có thể bù đắp dung lượng ban đầu thấp hơn do thay đổi kích thước bản đồ băm trước đó. Đối với công suất ban đầu cao hơn, chúng dường như không quan trọng lắm.
- Về thời gian nhận: kết quả hơi hỗn loạn ở đây. Không có nhiều điều để kết luận. Nó dường như phụ thuộc rất nhiều vào tỷ lệ tinh tế giữa chồng chéo mã băm, dung lượng ban đầu và hệ số tải, với một số thiết lập được cho là xấu hoạt động tốt và thiết lập tốt hoạt động kém.
- Tôi dường như đầy tào lao khi nói đến các giả định về hiệu suất Java. Sự thật là, trừ khi bạn điều chỉnh cài đặt của mình một cách hoàn hảo để triển khai
HashMap
, kết quả sẽ ở khắp nơi. Nếu có một điều cần gỡ bỏ điều này, đó là kích thước ban đầu mặc định là 16 hơi ngớ ngẩn đối với bất kỳ thứ gì ngoại trừ các bản đồ nhỏ nhất, vì vậy hãy sử dụng một hàm tạo đặt kích thước ban đầu nếu bạn có bất kỳ ý tưởng nào về thứ tự kích thước nó sẽ.
- Chúng tôi đang đo bằng nano giây ở đây. Thời gian trung bình tốt nhất cho mỗi 10 lần đặt là 1179 ns và kém nhất là 5105 ns trên máy của tôi. Thời gian trung bình tốt nhất cho mỗi 10 người nhận được là 547 ns và tồi tệ nhất là 3484 ns. Đó có thể là yếu tố 6 khác biệt, nhưng chúng ta đang nói chưa đến một phần nghìn giây. Trên các bộ sưu tập lớn hơn rất nhiều so với những gì mà áp phích gốc đã nghĩ đến.
Vâng, đó là nó. Tôi hy vọng mã của tôi không có một số giám sát khủng khiếp làm mất hiệu lực của mọi thứ tôi đã đăng ở đây. Điều này thật thú vị và tôi đã học được rằng cuối cùng thì bạn cũng có thể dựa vào Java để thực hiện công việc của nó hơn là mong đợi nhiều sự khác biệt từ những tối ưu hóa nhỏ. Điều đó không có nghĩa là không nên tránh một số thứ, nhưng sau đó chúng ta chủ yếu nói về việc xây dựng các Chuỗi dài trong vòng lặp for, sử dụng các cấu trúc dữ liệu sai và đưa ra thuật toán O (n ^ 3).