Java 具有快速搜索和慢速插入/删除功能的内存有效整数列表
我试图为正整数(数百万个元素)的排序列表找到最佳值。要求(按重要性排序):Java 具有快速搜索和慢速插入/删除功能的内存有效整数列表,java,algorithm,design-patterns,data-structures,Java,Algorithm,Design Patterns,Data Structures,我试图为正整数(数百万个元素)的排序列表找到最佳值。要求(按重要性排序): 内存占用小 快速O(日志n)search 插入/删除速度快于memcpy() 我正在考虑保留两个数组:一个用于搜索,一个用于插入。每隔几次操作,我就会重新组织主要的一次,并清理第二次。有什么想法吗?我走对了吗 注:没有重复的。它不需要是线程安全的。读会经常发生,而写会很少发生。整数在结构中的分布是不均匀的,这意味着一些结构只包含几个元素,而其他结构可能包含数百万个元素,它们的位置从零到0xffffff我想您应该使用 它具
O(日志n)
searchmemcpy()
注:没有重复的。它不需要是线程安全的。读会经常发生,而写会很少发生。整数在结构中的分布是不均匀的,这意味着一些结构只包含几个元素,而其他结构可能包含数百万个元素,它们的位置从零到
0xffffff
我想您应该使用
它具有以下特点:
Space O(M)
Search O(log log M)
Insert O(log log M)
Delete O(log log M)
我想你想用一个 它具有以下特点:
Space O(M)
Search O(log log M)
Insert O(log log M)
Delete O(log log M)
那链表呢?momory约束是int的大小+上一个和下一个指针的一点开销。至于插入和删除,所需的时间只需沿着列表往下看,直到找到一个比正在插入的小的,并将其放在记录之前。删除只需要更改“上一个”和“下一个”的指针,搜索和插入一样简单。链表呢?momory约束是int的大小+上一个和下一个指针的一点开销。至于插入和删除,所需的时间只需沿着列表往下看,直到找到一个比正在插入的小的,并将其放在记录之前。删除只需要更改“上一个”和“下一个”的指针,搜索和插入一样简单。您能使用
char[65536][]
吗?其中,顶部或底部16位是其他16位数组的索引。这可能会使用少于4*X的每个条目
查找
private final char[][] bitsArray = new char[65536][];
public int countFor(int num) {
int topBits = num >>> 16;
int lowerBits = num & 0xFFFF;
char[] lowerBitsArray = bitsArray[topBits];
int count = 0;
for(char l : lowerBitsArray)
if(l == lowerBits)
count++;
return count;
}
如果计数不能超过1,则位集可能是更好的选择。(可能是一个位集数组,具体取决于数据的模式)例如,如果要记录看到的IP地址,可能不需要担心0、10、127.*或224-255.*
无论是
int[]
还是char[]
都可以更快地访问,包括对int的强制转换
public static void main(String... args) {
char[] chars = new char[1000000];
for (int i = 0; i < 5; i++)
timeSum(chars);
int[] ints = new int[1000000];
for (int i = 0; i < 5; i++)
timeSum(ints);
}
private static int timeSum(char[] chars) {
long start = System.nanoTime();
int sum = 0;
for (char ch : chars) {
sum += ch;
}
long time = System.nanoTime() - start;
System.out.printf("Took %,d us to sum %,d chars%n", time / 1000, chars.length);
return sum;
}
private static int timeSum(int[] ints) {
long start = System.nanoTime();
int sum = 0;
for (int i : ints) {
sum += i;
}
long time = System.nanoTime() - start;
System.out.printf("Took %,d us to sum %,d ints%n", time / 1000, ints.length);
return sum;
}
我的结论是缓存效率比强制转换成本更重要。您可以使用
char[65536][]
?其中,顶部或底部16位是其他16位数组的索引。这可能会使用少于4*X的每个条目
查找
private final char[][] bitsArray = new char[65536][];
public int countFor(int num) {
int topBits = num >>> 16;
int lowerBits = num & 0xFFFF;
char[] lowerBitsArray = bitsArray[topBits];
int count = 0;
for(char l : lowerBitsArray)
if(l == lowerBits)
count++;
return count;
}
如果计数不能超过1,则位集可能是更好的选择。(可能是一个位集数组,具体取决于数据的模式)例如,如果要记录看到的IP地址,可能不需要担心0、10、127.*或224-255.*
无论是
int[]
还是char[]
都可以更快地访问,包括对int的强制转换
public static void main(String... args) {
char[] chars = new char[1000000];
for (int i = 0; i < 5; i++)
timeSum(chars);
int[] ints = new int[1000000];
for (int i = 0; i < 5; i++)
timeSum(ints);
}
private static int timeSum(char[] chars) {
long start = System.nanoTime();
int sum = 0;
for (char ch : chars) {
sum += ch;
}
long time = System.nanoTime() - start;
System.out.printf("Took %,d us to sum %,d chars%n", time / 1000, chars.length);
return sum;
}
private static int timeSum(int[] ints) {
long start = System.nanoTime();
int sum = 0;
for (int i : ints) {
sum += i;
}
long time = System.nanoTime() - start;
System.out.printf("Took %,d us to sum %,d ints%n", time / 1000, ints.length);
return sum;
}
我的结论是缓存效率比cast成本更重要。这实际上是一个有趣且不平凡的问题。最佳答案将取决于您的具体要求以及您执行的操作的精确组合 如果数据密集且不允许重复,则大位图可能是最佳选择。只需设置一个位来显示每个可能整数值的存在/不存在。这种方法的读写速度都非常快,而且都是O(1),但是内存使用率显然是由您拥有的范围有多大/数据的稀疏程度决定的 如果数据密集且允许重复/common,则存储每个可能值的出现次数的数组可能工作良好。在性能上与位图方法类似,但假设发生计数为int,则可能需要32倍的内存 如果您是重读且数据稀疏,则基于排序数组的方法(使用二进制搜索进行查找)可能是最好的方法。如果您了解值的粗略分布,那么您可以通过使用启发式来猜测目标值在数组中的可能位置,从而更快地完成任务(例如,如果您利用分布大致均匀的知识,您可以大大超过log2(N))
如果有大量写操作且数据稀疏,则可能需要基于树的结构,该结构基于整数中位的子集进行拆分(例如,每个节点上下一个最重要的5位上的32路trie拆分)。Clojure的持久化数据结构使用了这项技术,效果非常好。这实际上是一个有趣且不平凡的问题。最佳答案将取决于您的具体要求以及您执行的操作的精确组合 如果数据密集且不允许重复,则大位图可能是最佳选择。只需设置一个位来显示每个可能整数值的存在/不存在。这种方法的读写速度都非常快,而且都是O(1),但是内存使用率显然是由您拥有的范围有多大/数据的稀疏程度决定的 如果数据密集且允许重复/common,则存储每个可能值的出现次数的数组可能工作良好。在性能上与位图方法类似,但假设发生计数为int,则可能需要32倍的内存 如果您是重读且数据稀疏,则基于排序数组的方法(使用二进制搜索进行查找)可能是最好的方法。如果您了解值的粗略分布,则可以通过使用启发式来猜测目标值在数组中的可能位置(例如,您可以