为什么Java';s ArrayList';s删除功能的成本似乎很低?
我有一个函数,可以处理一个非常大的列表,超过250000项。对于大多数项目,它只是在位置x处替换项目。然而,对于大约5%的人来说,它必须将他们从名单中删除 使用LinkedList似乎是避免昂贵删除的最明显的解决方案。然而,随着时间的推移,通过索引访问LinkedList的速度自然会越来越慢。这里的成本是分钟(而且很多) 在该LinkedList上使用迭代器也很昂贵,因为我似乎需要一个单独的副本,以避免在编辑该列表时出现迭代器并发问题。这里的费用是几分钟 然而,在这里,我的头脑有点崩溃。如果我更改为ArrayList,它几乎会立即运行 对于包含297515个元素的列表,删除11958个元素并修改所有其他元素需要909毫秒。我验证了结果列表的大小确实如预期的那样是28557,并且包含了我需要的更新信息为什么Java';s ArrayList';s删除功能的成本似乎很低?,java,performance,optimization,arraylist,Java,Performance,Optimization,Arraylist,我有一个函数,可以处理一个非常大的列表,超过250000项。对于大多数项目,它只是在位置x处替换项目。然而,对于大约5%的人来说,它必须将他们从名单中删除 使用LinkedList似乎是避免昂贵删除的最明显的解决方案。然而,随着时间的推移,通过索引访问LinkedList的速度自然会越来越慢。这里的成本是分钟(而且很多) 在该LinkedList上使用迭代器也很昂贵,因为我似乎需要一个单独的副本,以避免在编辑该列表时出现迭代器并发问题。这里的费用是几分钟 然而,在这里,我的头脑有点崩溃。如果我更
为什么这么快?我查看了JDK6中ArrayList的源代码,它似乎按照预期使用了arraycopy函数。我很想理解为什么ArrayList在这里工作得这么好,而常识似乎表明,用于此任务的数组是一个糟糕的想法,需要移动数十万项。我认为性能上的差异可能归结为ArrayList支持随机访问,而LinkedList不支持 如果我想获得(1000)个ArrayList,我会指定一个特定的索引来访问它,但是LinkedList不支持它,因为它是通过节点引用组织的
如果我调用LinkedList的get(1000),它将迭代整个列表,直到If找到索引1000。如果LinkedList中有大量项目,这可能会非常昂贵。有趣且意外的结果。这只是一个假设,但是 平均来说,删除一个数组元素需要将列表的一半(后面的所有内容)移回一个元素。如果每个项目都是指向对象的64位指针(8字节),则这意味着复制125000个项目x每个指针8字节=1 MB 一个现代的CPU可以很快地将一个连续的1MB内存块复制到RAM中 与每次访问都在链表上循环(需要比较、分支和其他不利于CPU的活动)相比,RAM拷贝速度更快
您应该真正尝试独立地对各种操作进行基准测试,并查看它们在各种列表实现中的效率。如果您愿意,请在此处分享您的结果 数组复制是一个相当无用的操作。这是在一个非常基本的级别上完成的(这是一个java本地静态方法),您还没有达到性能变得真正重要的范围 在您的示例中,一个大小为150000(平均)的数组可以复制大约12000次。这不需要太多时间。我在我的笔记本电脑上测试了它,不到500毫秒 更新我使用以下代码在笔记本电脑(英特尔P8400)上进行测量 我在这里故意跳过一些实现细节,只是为了解释根本区别 要删除M个元素列表中的第N个元素,LinkedList实现将导航到此元素,然后简单地删除它并相应地更新N-1和N+1元素的指针。第二个操作非常简单,但要达到这一点需要花费时间 然而,对于ArrayList,访问时间是瞬时的,因为它由数组支持,这意味着连续的内存空间。您可以直接跳转到正确的内存地址来执行以下操作:
- 重新分配一个新的M-1元素数组
- 将从0到N-1的所有内容放在新arraylist数组的索引0处
- 将所有N+1到M放在arraylist数组的索引N处
要在其中添加一桶盐和香料:
- 请参阅和条目,它们描述了您的问题
- 请记住,使用需要连续内存的内存结构需要连续内存。这意味着您的虚拟内存将需要能够分配连续块。或者,即使使用Java,您也会看到JVM在低级别崩溃时出现了一个模糊的OutOfMemoryException,导致JVM崩溃
- 将所需元素复制到新列表中
- 使用
从Iterator.remove()
ArrayList
- 使用
从迭代器.remove()
链接列表中删除不需要的元素
- 将列表压缩到位(将所需元素移动到较低的位置)
- 按索引删除(
)在List.Remove(int)
ArrayList上
- 在
链接列表上按索引删除(
)列表。删除(int)
点的随机实例填充列表,并使用一个过滤条件(基于散列码),该条件将接受95%的元素,并拒绝剩余的5%(相同的比例)
import java.util.Random;
public class PerformanceArrayCopy {
public static void main(String[] args) {
int[] lengths = new int[] { 10000, 50000, 125000, 250000 };
int[] loops = new int[] { 1000, 5000, 10000, 20000 };
for (int length : lengths) {
for (int loop : loops) {
Object[] list1 = new Object[length];
Object[] list2 = new Object[length];
for (int k = 0; k < 100; k++) {
System.arraycopy(list1, 0, list2, 0, list1.length);
}
int[] len = new int[loop];
int[] ofs = new int[loop];
Random rnd = new Random();
for (int k = 0; k < loop; k++) {
len[k] = rnd.nextInt(length);
ofs[k] = rnd.nextInt(length - len[k]);
}
long n = System.nanoTime();
for (int k = 0; k < loop; k++) {
System.arraycopy(list1, ofs[k], list2, ofs[k], len[k]);
}
n = System.nanoTime() - n;
System.out.print("length: " + length);
System.out.print("\tloop: " + loop);
System.out.print("\truntime [ms]: " + n / 1000000);
System.out.println();
}
}
}
}
length: 10000 loop: 10000 runtime [ms]: 47
length: 50000 loop: 10000 runtime [ms]: 228
length: 125000 loop: 10000 runtime [ms]: 575
length: 250000 loop: 10000 runtime [ms]: 1198
CopyIntoNewListWithIterator : 4.24ms
CopyIntoNewListWithoutIterator: 3.57ms
FilterLinkedListInPlace : 4.21ms
RandomRemoveByIndex : 312.50ms
SequentialRemoveByIndex : 33632.28ms
ShiftDown : 3.75ms
CopyIntoNewListWithIterator : 92.49ms
CopyIntoNewListWithoutIterator: 71.77ms
FilterLinkedListInPlace : 15.73ms
ShiftDown : 11.86ms
import java.awt.Point;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.TreeMap;
public class ListBenchmark {
public static void main(String[] args) {
Random rnd = new SecureRandom();
Map<String, Long> timings = new TreeMap<String, Long>();
for (int outerPass = 0; outerPass < 10; ++ outerPass) {
List<FilterStrategy> strategies =
Arrays.asList(new CopyIntoNewListWithIterator(),
new CopyIntoNewListWithoutIterator(),
new FilterLinkedListInPlace(),
new RandomRemoveByIndex(),
new SequentialRemoveByIndex(),
new ShiftDown());
for (FilterStrategy strategy: strategies) {
String strategyName = strategy.getClass().getSimpleName();
for (int innerPass = 0; innerPass < 10; ++ innerPass) {
strategy.populate(rnd);
if (outerPass >= 5 && innerPass >= 5) {
Long totalTime = timings.get(strategyName);
if (totalTime == null) totalTime = 0L;
timings.put(strategyName, totalTime - System.currentTimeMillis());
}
Collection<Point> filtered = strategy.filter();
if (outerPass >= 5 && innerPass >= 5) {
Long totalTime = timings.get(strategyName);
timings.put(strategy.getClass().getSimpleName(), totalTime + System.currentTimeMillis());
}
CHECKSUM += filtered.hashCode();
System.err.printf("%-30s %d %d %d%n", strategy.getClass().getSimpleName(), outerPass, innerPass, filtered.size());
strategy.clear();
}
}
}
for (Map.Entry<String, Long> e: timings.entrySet()) {
System.err.printf("%-30s: %9.2fms%n", e.getKey(), e.getValue() * (1.0/25.0));
}
}
public static volatile int CHECKSUM = 0;
static void populate(Collection<Point> dst, Random rnd) {
for (int i = 0; i < INITIAL_SIZE; ++ i) {
dst.add(new Point(rnd.nextInt(), rnd.nextInt()));
}
}
static boolean wanted(Point p) {
return p.hashCode() % 20 != 0;
}
static abstract class FilterStrategy {
abstract void clear();
abstract Collection<Point> filter();
abstract void populate(Random rnd);
}
static final int INITIAL_SIZE = 100000;
private static class CopyIntoNewListWithIterator extends FilterStrategy {
public CopyIntoNewListWithIterator() {
list = new ArrayList<Point>(INITIAL_SIZE);
}
@Override
void clear() {
list.clear();
}
@Override
Collection<Point> filter() {
ArrayList<Point> dst = new ArrayList<Point>(list.size());
for (Point p: list) {
if (wanted(p)) dst.add(p);
}
return dst;
}
@Override
void populate(Random rnd) {
ListBenchmark.populate(list, rnd);
}
private final ArrayList<Point> list;
}
private static class CopyIntoNewListWithoutIterator extends FilterStrategy {
public CopyIntoNewListWithoutIterator() {
list = new ArrayList<Point>(INITIAL_SIZE);
}
@Override
void clear() {
list.clear();
}
@Override
Collection<Point> filter() {
int inputSize = list.size();
ArrayList<Point> dst = new ArrayList<Point>(inputSize);
for (int i = 0; i < inputSize; ++ i) {
Point p = list.get(i);
if (wanted(p)) dst.add(p);
}
return dst;
}
@Override
void populate(Random rnd) {
ListBenchmark.populate(list, rnd);
}
private final ArrayList<Point> list;
}
private static class FilterLinkedListInPlace extends FilterStrategy {
public String toString() {
return getClass().getSimpleName();
}
FilterLinkedListInPlace() {
list = new LinkedList<Point>();
}
@Override
void clear() {
list.clear();
}
@Override
Collection<Point> filter() {
for (Iterator<Point> it = list.iterator();
it.hasNext();
) {
Point p = it.next();
if (! wanted(p)) it.remove();
}
return list;
}
@Override
void populate(Random rnd) {
ListBenchmark.populate(list, rnd);
}
private final LinkedList<Point> list;
}
private static class RandomRemoveByIndex extends FilterStrategy {
public RandomRemoveByIndex() {
list = new ArrayList<Point>(INITIAL_SIZE);
}
@Override
void clear() {
list.clear();
}
@Override
Collection<Point> filter() {
for (int i = 0; i < list.size();) {
if (wanted(list.get(i))) {
++ i;
} else {
list.remove(i);
}
}
return list;
}
@Override
void populate(Random rnd) {
ListBenchmark.populate(list, rnd);
}
private final ArrayList<Point> list;
}
private static class SequentialRemoveByIndex extends FilterStrategy {
public SequentialRemoveByIndex() {
list = new LinkedList<Point>();
}
@Override
void clear() {
list.clear();
}
@Override
Collection<Point> filter() {
for (int i = 0; i < list.size();) {
if (wanted(list.get(i))) {
++ i;
} else {
list.remove(i);
}
}
return list;
}
@Override
void populate(Random rnd) {
ListBenchmark.populate(list, rnd);
}
private final LinkedList<Point> list;
}
private static class ShiftDown extends FilterStrategy {
public ShiftDown() {
list = new ArrayList<Point>();
}
@Override
void clear() {
list.clear();
}
@Override
Collection<Point> filter() {
int inputSize = list.size();
int outputSize = 0;
for (int i = 0; i < inputSize; ++ i) {
Point p = list.get(i);
if (wanted(p)) {
list.set(outputSize++, p);
}
}
list.subList(outputSize, inputSize).clear();
return list;
}
@Override
void populate(Random rnd) {
ListBenchmark.populate(list, rnd);
}
private final ArrayList<Point> list;
}
}