我有:非常大的数组:{o1,o2,o3,...,o100000}。在某些情况下,元素序列具有相同的字段值。我的意思是o1.getField()
等于o2.getField()
等于o3.getField()
。让我们说o1,o2和o3 类似 。
我想:对每个类似的元素子阵列进行一些事后处理(例如{o1, o2, o3}
,{o4, o5}
,...)。
这很重要:由于数组非常大,缓存(意味着将数组移动到另一个集合或者部署其中)是不可接受的!
问题:最优雅的方式是什么?
P.S。我没有必要划分整个数组而只需要进行postAction。我可以获得第一个{o1, o2, o3}
,制作postAction,而不是获得第二个等等......
答案 0 :(得分:1)
我首先要挑战"阵列无法复制"前提。如果您使用System.arrayCopy()
!
这是我写的一个小样本程序,用于创建,填充和复制100,000,000个元素的数组。
public static void main(final String[] args) {
final Stopwatch stopwatch = new Stopwatch().start();
final String[] arr = new String[100_000_000];
Arrays.fill(arr, "foo");
final String[] arr2 = new String[arr.length];
System.arraycopy(arr, 0, arr2, 0, arr.length);
stopwatch.stop();
System.out.println(stopwatch.elapsed(MILLISECONDS));
}
在我令人印象深刻的机器上,这需要683毫秒。
如果这些都不是,我会选择一些复制解决方案。
答案 1 :(得分:1)
我假设你正在处理T
类型的对象并且有一个方法isSimilar(T o1, T o2)
。
我还假设你有一个Processor<T>
课,其方法为postAction(Iterator<T> i)
(可以适应postAction(Iterable<T> i)
)。
然后我会继续这些行。它只是一个方向,不幸的是我没有任何东西可以测试甚至编译。
public <T> void process(T[] array, Processor<T> p) {
for (int i=0, j=1; i<array.length && j<array.length; i=j, j++) {
while (j<array.length && isSimilar(array[i], array[j])) {
j++;
}
ArrayIterator<T> ai = new ArrayIterator<T>(array, i, j-1);
p.postAction(ai);
}
}
class ArrayIterator<T> implements Iterator<T> {
T[] array;
int current;
int end;
public ArrayIterator(T[] a, int s, int e) {
array = a;
current = s;
end = e;
}
public boolean hasNext() {
return current <= end;
}
public T next() {
return array[current++];
}
}
如果postAction
采用List
或其他强加的集合,您可以创建由数组备份的此类集合,例如Arrays.asList()
,但它将是更多的样板代码。如果postAction
采用数组,我认为你必须复制......
答案 2 :(得分:1)
我们有这个班级
public static class MisteriousItem {
public int oddlyFamiliarValue;
public MisteriousItem(int oddlyFamiliarValue) {
this.oddlyFamiliarValue = oddlyFamiliarValue;
}
}
我们用来放入数组的:
Random rand = new Random();
MisteriousItem[] magicBox = new MisteriousItem[1_000];
for (int i = 0; i < magicBox.length; i++) {
magicBox[i] = new MisteriousItem(rand.nextInt(3));
}
然后我们准备了一些价值
List<Entry<Integer, String>> something;
something = new ArrayList<>();
MisteriousItem x = magicBox[0];
StringBuilder sb = new StringBuilder("{");
sb.append(x.oddlyFamiliarValue);
int id = 0;
我知道你说没有副本,但这不是算法的一部分,只是与熟悉的对象有关
现在我们迭代
for (int i = 1; i < magicBox.length; i++) {
MisteriousItem mi = magicBox[i];
if (mi.oddlyFamiliarValue == x.oddlyFamiliarValue) {
sb.append(",");
sb.append(id++);
} else {
sb.append("}");
something.add(new Entry<>(x.oddlyFamiliarValue, sb.toString()));
x = mi;
sb = new StringBuilder("{");
sb.append(id++);
}
}
正如您所看到的,我们正在获取第一个对象并开始处理,然后从第二个项开始迭代整个数组,在执行任何操作之前,我们检查它是否与先前的对象相似。然后我们根据比较的结果采取行动。 如果您正在寻找一些更优雅的方式,请发表评论并说明应该有什么不同。
修改强>
也许这就是你正在寻找的更多
int end=0;
int start= 1;
MisteriousItem x = magicBox[0];
for (int i = 1; i < magicBox.length; i++) {
MisteriousItem mi = magicBox[i];
if (mi.oddlyFamiliarValue == x.oddlyFamiliarValue) {
end++;
} else {
process(magicBox,start,end);
//process lost starting from start(inclusive) to end(exclusive)
start=i;
}
}
答案 3 :(得分:0)
对getField
进行就地排序,然后按顺序进行迭代,仅在getField
更改时调用操作。我相信这是唯一不会产生相同长度数组开销的解决方案(因为我能想到的任何其他解决方案都涉及一些机制来跟踪您使用的getField
值。) / p>