在我的考试的第一个问题中:我正在开展一项小任务,我需要在数组中存储大约500万个+元素。
但是,我遇到了堆空间问题。你能帮我解决一下这个最优的存储算法吗?
我找到了“BitSet”,但我不知道如何使用它。
步骤1 - 创建3个长[(最小100M +)
的long []数组步骤2 - 初始值应该是随机生成的,不是排序的,可能包含重复项
步骤3 - 在init之后将它们随机合并(3个long []数组)
步骤4 - 应在输出中删除重复项目
我写了几件事:
package exam1;
import java.time.Duration;
import java.time.Instant;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Random;
/**
*
* @author Furkan
*/
//VM OPTIONS -> -Xincgc -Xmx4g -Xms4g
public final class Exam1 {
private static final int LENGTH = 100000000;
private volatile long[] m_testArr1 = null;
private volatile long[] m_testArr2 = null;
private volatile long[] m_testArr3 = null;
private volatile long[] m_merged = null;
private Random m_r = new Random(System.currentTimeMillis());
public static void main(String[] args) {
Exam1 exam = new Exam1();
Instant start1 = Instant.now();
System.out.println("Fill Started");
exam.Fill();
Instant end1 = Instant.now();
System.out.println("Fill Ended : " + Duration.between(start1, end1));
Instant start2 = Instant.now();
System.out.println("Merge Started");
exam.Merge();
Instant end2 = Instant.now();
System.out.println("Merge Ended : " + Duration.between(start1, end1));
Instant start3 = Instant.now();
System.out.println("DupRemove Started");
exam.DupRemove();
Instant end3 = Instant.now();
System.out.println("DupRemove Ended : " + Duration.between(start1, end1));
}
private void Fill(){
this.m_testArr1 = new long[Exam1.LENGTH];
this.m_testArr2 = new long[Exam1.LENGTH];
this.m_testArr3 = new long[Exam1.LENGTH];
for (int i = 0; i < Exam1.LENGTH; i++) {
this.m_testArr1[i] = this.m_r.nextLong();
this.m_testArr2[i] = this.m_r.nextLong();
this.m_testArr3[i] = this.m_r.nextLong();
}
}
private void Merge(){
this.m_merged = this.TryMerge(this.m_testArr1, this.m_testArr2, this.m_testArr3);
}
private void DupRemove(){
this.m_merged = this.RemoveDuplicates(this.m_merged);
}
public long[] TryMerge(long[] arr1, long[] arr2, long[] arr3){
int aLen = arr1.length;
int bLen = arr2.length;
int cLen = arr3.length;
int len = aLen + bLen + cLen;
//TODO: Use BitSize for RAM optimize. IDK how to use...
//OutOfMemory Exception on this line.
long[] mergedArr = new long[len];
this.m_merged = new long[len];
//long[] mergedArr = (long[]) Array.newInstance(long.class, aLen+bLen+cLen);
System.arraycopy(arr1, 0, mergedArr, 0, aLen);
System.arraycopy(arr2, 0, mergedArr, aLen, bLen);
System.arraycopy(arr3, 0, mergedArr, (aLen + bLen), cLen);
return mergedArr;
}
//!!!NOT WORKING!!!
private long[] RemoveDuplicates(long[] arr){
HashSet<Long> set = new HashSet<Long>();
final int len = arr.length;
for(int i = 0; i < len; i++){
set.add(arr[i]);
}
long[] clean = new long[set.size()];
int i = 0;
for (Iterator<Long> it = set.iterator(); it.hasNext();) {
clean[i++] = it.next();
}
return clean;
}
}
原始问题;
- 实现一个高效的methot来合并3组非常大(长度:100M +)的长[]数组。
- 随机生成但未排序的输入数据可能包含重复数据
- 应在输出中删除重复项目。
(我有8 GB RAM)
运行Args:-Xincgc -Xmx4g -Xms4g
异常:线程“main”中的异常java.lang.OutOfMemoryError:测试时的Java堆空间。
答案 0 :(得分:3)
由于您的空间有限,并且假设您允许修改3个随机数组,我建议如下。
对于3个阵列中的每一个:
对数组进行排序,例如使用Arrays.sort()
。
通过将非重复数字压缩到开头来消除重复
例如。如果你有{1,2,2,3,3}
,则压缩为{1,2,3,?,?}
,长度为3,其中?
表示价值并不重要。
(可选)移动到正确大小的数组,并丢弃原始数组,以释放结果数组的空间。
创建大小为len1 + len2 + len3
的结果数组。
将3个数组合并到结果中,消除了数组之间的重复
例如。如果您有{1,3,5}
,{1,2,3}
,则最终会得到长度为4的{1,2,3,5,?,?}
。
如果需要,将结果复制到正确大小的新数组 如果内存不足,请在执行此操作之前释放3个原始阵列以释放空间。
答案 1 :(得分:1)
使用Bloom filter来识别可能的重复项,然后使用哈希集来清除可能重复项集中的误报,即
foreach源数组元素,将其添加到Bloom过滤器;如果元素(可能)包含在bloom过滤器中,则将其添加到哈希集中,否则将其添加到合并的数组中。处理完所有源数组后,检查合并数组的每个元素以查看它是否在哈希集中,从哈希集中删除重复项。最后,将哈希集的所有剩余元素添加到合并数组中。
Guava有一个可以使用的布隆过滤器数据结构。
答案 2 :(得分:0)
如果您没有足够的内存来存储您需要通过分析业务需求和现实情况来更改内容的所有数据。
也许您应该像其他人建议的那样使用一些内置的集合框架。
或者如果不允许(无论出于何种原因),您应该将数据保存在除内存之外的其他位置。例如。
现在,你在一个文件中有一个已排序的重复自由合并数组,如果有必要,你可以在删除原件后阅读。