自定义HashMap的代码问题(Custom HashMap Code Issue)

2019-06-26 03:32发布

我有以下代码,其中我用HashMap中(使用两个平行的阵列),用于存储键值对(键可以有多个值)。 现在,我要存储和载入以备将来使用,这就是为什么我存储和使用文件通道加载它。 问题与此代码是:我可以存储近120百万键值对的在我的8 GB的服务器(实际上,我可以拨款近5 GB出8 GB的我的JVM,并且在这两个平行阵列需要将近2.5 GB,其他存储器用于我的代码的各种处理)。 但是,我必须存储近600/700百万键 - 值对。 可以anybdoy帮助我如何修改这个代码,所以我可以存储近600/700百万键 - 值对。 或任何评论将是我很好。 还有一点,我必须加载和HashMap中从内存中存储/。 这需要使用文件通道有点长的时间。 由于每个堆栈溢出的各种建议,我没有找到一个更快的。 我已经使用的ObjectOutputStream,拉链输出流也然而,除了以下代码慢。 反正是有这两个平行阵列存储以这样的方式从而加载时间会快很多。 我在我的代码测试情况如下。 对此有何评论也将是对我很有帮助。

import java.io.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Arrays;
import java.util.Random;
import java.nio.*;
import java.nio.channels.FileChannel;
import java.io.RandomAccessFile;

public class Test {

    public static void main(String args[]) {


        try {

            Random randomGenerator = new Random();

            LongIntParallelHashMultimap lph = new LongIntParallelHashMultimap(220000000, "xx.dat", "yy.dat");

            for (int i = 0; i < 110000000; i++) {
                lph.put(i, randomGenerator.nextInt(200000000));
            }

            lph.save();

            LongIntParallelHashMultimap lphN = new LongIntParallelHashMultimap(220000000, "xx.dat", "yy.dat");
            lphN.load();

            int tt[] = lphN.get(1);

            System.out.println(tt[0]);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class LongIntParallelHashMultimap {

    private static final long NULL = -1L;
    private final long[] keys;
    private final int[] values;
    private int size;
    private int savenum = 0;
    private String str1 = "";
    private String str2 = "";

    public LongIntParallelHashMultimap(int capacity, String st1, String st2) {
        keys = new long[capacity];
        values = new int[capacity];
        Arrays.fill(keys, NULL);
        savenum = capacity;
        str1 = st1;
        str2 = st2;
    }

    public void put(long key, int value) {
        int index = indexFor(key);
        while (keys[index] != NULL) {
            index = successor(index);
        }
        keys[index] = key;
        values[index] = value;
        ++size;
    }

    public int[] get(long key) {
        int index = indexFor(key);
        int count = countHits(key, index);
        int[] hits = new int[count];
        int hitIndex = 0;

        while (keys[index] != NULL) {
            if (keys[index] == key) {
                hits[hitIndex] = values[index];
                ++hitIndex;
            }
            index = successor(index);
        }

        return hits;
    }

    private int countHits(long key, int index) {
        int numHits = 0;
        while (keys[index] != NULL) {
            if (keys[index] == key) {
                ++numHits;
            }
            index = successor(index);
        }
        return numHits;
    }

    private int indexFor(long key) {
        return Math.abs((int) ((key * 5700357409661598721L) % keys.length));
    }

    private int successor(int index) {
        return (index + 1) % keys.length;
    }

    public int size() {
        return size;
    }

    public void load() {
        try {
            FileChannel channel2 = new RandomAccessFile(str1, "r").getChannel();
            MappedByteBuffer mbb2 = channel2.map(FileChannel.MapMode.READ_ONLY, 0, channel2.size());
            mbb2.order(ByteOrder.nativeOrder());
            assert mbb2.remaining() == savenum * 8;
            for (int i = 0; i < savenum; i++) {
                long l = mbb2.getLong();
                keys[i] = l;
            }
            channel2.close();

            FileChannel channel3 = new RandomAccessFile(str2, "r").getChannel();
            MappedByteBuffer mbb3 = channel3.map(FileChannel.MapMode.READ_ONLY, 0, channel3.size());
            mbb3.order(ByteOrder.nativeOrder());
            assert mbb3.remaining() == savenum * 4;
            for (int i = 0; i < savenum; i++) {
                int l1 = mbb3.getInt();
                values[i] = l1;
            }
            channel3.close();
        } catch (Exception e) {
            System.out.println(e);
        }
    }

    public void save() {
        try {
            FileChannel channel = new RandomAccessFile(str1, "rw").getChannel();
            MappedByteBuffer mbb = channel.map(FileChannel.MapMode.READ_WRITE, 0, savenum * 8);
            mbb.order(ByteOrder.nativeOrder());

            for (int i = 0; i < savenum; i++) {
                mbb.putLong(keys[i]);
            }
            channel.close();

            FileChannel channel1 = new RandomAccessFile(str2, "rw").getChannel();
            MappedByteBuffer mbb1 = channel1.map(FileChannel.MapMode.READ_WRITE, 0, savenum * 4);
            mbb1.order(ByteOrder.nativeOrder());

            for (int i = 0; i < savenum; i++) {
                mbb1.putInt(values[i]);
            }
            channel1.close();
        } catch (Exception e) {
            System.out.println("IOException : " + e);
        }
    }
}

Answer 1:

我怀疑这是可能的,因为你已经声明的数据类型。 只要乘以原始类型的大小。

每行需要4个字节来存储一个int和8个字节来存储一个长。 6个亿行*每行12个字节= 7200 MB = 7.03 GB。 你说你可以分配5 GB的JVM。 所以,即使是所有的堆和存储仅此自定义HashMap的,它不适合。 考虑收缩所涉及的数据类型的大小或存储它的地方,除了RAM。



Answer 2:

拥有对数据库的磁盘,而不是在内存中。 重写你的操作,使得他们不数组操作,但对缓冲区,而不是工作。 然后你就可以打开一个足够大的文件,并有操作访问他们所需要使用映射缓冲区的部分。 尝试当你执行一些最近映射内存区域的高速缓存应用程序是否执行得更好,所以你不会有映射和取消映射共同的区域过于频繁,反而可以让他们在映射。

这应该给你最好的两个世界,在磁盘和RAM的:

  • 对数据结构的任何部分的随机存取是容易实现
  • 访问表中的经常使用的部分将被缓存
  • 表中的很少使用部分将不会占用任何存储器

正如你所看到的,这依赖于地方很多:如果某些键比其他人更常见,事情将表现良好,而很好地分散密钥将导致每次访问一个新的磁盘操作。 因此,虽然漂亮的分布是理想的大多数内存中的哈希地图,这映射经常使用的按键类似位置的其他结构将在这里有更好的表现。 这些将与冲突处理干扰,虽然。



Answer 3:

更好地使用内存数据库SQLite的一样,这将提供良好的结果。



文章来源: Custom HashMap Code Issue