- 情况:**
针对一个(庞大的)元素数组需要高效地转换为一个扩展了一个值的List的问题,设计了一个简化的AbstractList实现,并将其用于一个专用的方法中,取得了很好的效果。
- 问题:**
简单地应用Arrays.asList(String element, String[] elements)
是不会起作用的(这样会非常快),尝试任何其他看似简单的方法都会失败,因为会出现严重的性能问题。
- 请考虑以下示例,其中没有一个执行良好:**
protected static <T> List<T> slowAsList(final T last, final T... elements) {
//1
return IntStream.rangeClosed(0, length)
.mapToObj(i -> i < length ? elements[i] : last)
.collect(Collectors.toList());
//2
return Stream.concat(Stream.of(first), Arrays.stream(elements))
.parallel()
.collect(Collectors.toList());
//3
return new ArrayList() {{ for (Object o : elements) add(o); add(last); }};
//4
final ArrayList arrayList = new ArrayList();
arrayList.add(last);
for (Object o : elements) arrayList.add(o);
return arrayList;
//5
final int arrayLen = elements.length + 1;
final Object[] array = Arrays.copyOf(elements, arrayLen);
for (int i = 1; i < arrayLen; i++) {
array[i] = elements[i - 1];
}
array[arrayLen - 1] = last;
return Arrays.asList(array);
}
该方法将在JUnit测试中停止,并且不会在可行的时间内完成:
@Timeout(15)
@Test
public void fastAsListTest() {
final Object[] testStrings1MwithNull = IntStream.rangeClosed(0, 1000000)
.mapToObj(i -> i == 1000000 ? null : "TEST" + i)
.toArray();
slowAsList(testStrings1MwithNull);
}
当用AbstractList
实现的变体替换方法中的实现时,测试将在几毫秒内完成,而不是:
protected static <T> List<T> fastAsList(final T last, final T... elements) {
if (elements == null) {
return null;
}
return new AbstractList<T>() {
@Override
public int size() {
return elements.length;
}
@Override
public T get(int index) {
return index < 0 ? null : elements[index];
}
};
}
编辑:这是原始代码,添加了first
元素
protected static List<Object> asList(final Object first, final Object[] elements) {
return new AbstractList<Object>() {
@Override
public int size() {
return elements.length + 1;
// return elements != null ? elements.length + 1 : 0;
}
@Override
public Object get(int index) {
return (index == 0) ? first : elements[index - 1];
// return (index == 0) ? first : elements!= null ? elements[index - 1] : null;
}
};
注解掉的代码显示了添加的null
检查,用于检查导致com.sun.jdi.InvocationException
的空值以及元素null
。
导致此问题的原因可能是什么,是否内存不足?
我已经将Xms/Xmx提升到2G/2G进行测试,在Windows中的总内存消耗约为83%(总共16G)。
2条答案
按热度按时间w1jd8yoj1#
首先:
你的性能测试很差。你的JVM没有预热,代码需要JIT等等。甚至有可能对被测方法的调用被编译掉--你不使用结果。学习如何使用JMH来更深入地了解每种替代方法的性能。
话虽如此:
所有的替代方法都非常慢,因为它们将源列表的元素复制到目标列表中,一次一个元素。你甚至没有设置输出列表的初始大小-它从一个小的默认值开始,当你添加元素时需要增长。同样,复制更多。
你还没有考虑过的一个选择是:
System.arraycopy
将源数组复制到目标数组中所需的位置。System.arraycopy
是复制数组的最快方法(等同于memcpy)在我的机器上,这比您的 Package 器实现要快(同样,没有预热JVM)
最重要的是:如果您的自定义 Package 器是一个性能优化,并且您不会 Package 一个 Package 器的 Package 器......,那么这样做似乎是合理的。
pkmbmrz72#
我的猜测是:AbstractList的返回对象是一个简单的Reference示例,返回对象的字段是一个巨大的数组。
换句话说,没有真正的拷贝。