我必须在内存中保留数千个字符串,以便在Java中串行访问。我应该把它们存储在数组中还是应该使用某种列表?
由于数组将所有数据保存在一个连续的内存块中(与list不同),使用数组存储数千个字符串会导致问题吗?
我必须在内存中保留数千个字符串,以便在Java中串行访问。我应该把它们存储在数组中还是应该使用某种列表?
由于数组将所有数据保存在一个连续的内存块中(与list不同),使用数组存储数千个字符串会导致问题吗?
当前回答
没有一个答案有我感兴趣的信息——重复扫描同一个数组很多很多次。必须为此做一个JMH测试。
结果(Java 1.8.0_66 x32,迭代普通数组至少比ArrayList快5倍):
Benchmark Mode Cnt Score Error Units
MyBenchmark.testArrayForGet avgt 10 8.121 ? 0.233 ms/op
MyBenchmark.testListForGet avgt 10 37.416 ? 0.094 ms/op
MyBenchmark.testListForEach avgt 10 75.674 ? 1.897 ms/op
Test
package my.jmh.test;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
@State(Scope.Benchmark)
@Fork(1)
@Warmup(iterations = 5, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 10)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class MyBenchmark {
public final static int ARR_SIZE = 100;
public final static int ITER_COUNT = 100000;
String arr[] = new String[ARR_SIZE];
List<String> list = new ArrayList<>(ARR_SIZE);
public MyBenchmark() {
for( int i = 0; i < ARR_SIZE; i++ ) {
list.add(null);
}
}
@Benchmark
public void testListForEach() {
int count = 0;
for( int i = 0; i < ITER_COUNT; i++ ) {
for( String str : list ) {
if( str != null )
count++;
}
}
if( count > 0 )
System.out.print(count);
}
@Benchmark
public void testListForGet() {
int count = 0;
for( int i = 0; i < ITER_COUNT; i++ ) {
for( int j = 0; j < ARR_SIZE; j++ ) {
if( list.get(j) != null )
count++;
}
}
if( count > 0 )
System.out.print(count);
}
@Benchmark
public void testArrayForGet() {
int count = 0;
for( int i = 0; i < ITER_COUNT; i++ ) {
for( int j = 0; j < ARR_SIZE; j++ ) {
if( arr[j] != null )
count++;
}
}
if( count > 0 )
System.out.print(count);
}
}
其他回答
我建议您使用分析器来测试哪个更快。
我个人的观点是你应该使用列表。
我在一个大型代码库中工作,之前的一组开发人员在任何地方都使用数组。这使得代码非常不灵活。在将大块数据转换为列表后,我们发现速度没有变化。
虽然建议使用数组列表的答案在大多数情况下是有意义的,但相对性能的实际问题还没有真正得到答案。
你可以用数组做以下几件事:
创建它 设置一个项目 买一件物品 克隆/复制它
一般的结论
虽然get和set操作在数组列表(resp。在我的机器上每次调用1和3纳秒),对于任何非密集的用途,使用ArrayList相对于数组的开销非常小。然而,有几件事要记住:
在列表上调整大小操作(当调用list.add(…)时)代价很高,应该尽可能将初始容量设置为适当的级别(注意,在使用数组时也会出现同样的问题) 在处理原语时,数组可以明显更快,因为它们可以避免许多装箱/拆箱转换 一个只在数组列表中获取/设置值的应用程序(不是很常见!)通过切换到数组可以看到超过25%的性能增益
详细的结果
下面是我在标准x86桌面机器上使用JDK 7使用jmh基准测试库(以纳秒为单位)测量这三个操作的结果。请注意,ArrayList在测试中从不调整大小,以确保结果具有可比性。这里有基准代码。
数组/ ArrayList创造
我运行了4个测试,执行以下语句:
createArray1: Integer[] array = new Integer[1]; createList1: List<Integer> List = new ArrayList<> (1); createArray10000: Integer[] array = new Integer[10000]; createList10000: List<Integer> List = new ArrayList<> (10000);
结果(以纳秒为单位,95%置信度):
a.p.g.a.ArrayVsList.CreateArray1 [10.933, 11.097]
a.p.g.a.ArrayVsList.CreateList1 [10.799, 11.046]
a.p.g.a.ArrayVsList.CreateArray10000 [394.899, 404.034]
a.p.g.a.ArrayVsList.CreateList10000 [396.706, 401.266]
结论:无明显差异。
get操作
我运行了2个测试,执行以下语句:
返回list.get(0); 返回数组[0];
结果(以纳秒为单位,95%置信度):
a.p.g.a.ArrayVsList.getArray [2.958, 2.984]
a.p.g.a.ArrayVsList.getList [3.841, 3.874]
结论:从数组中获取信息比从ArrayList中获取信息快25%,尽管差异仅在1纳秒的量级上。
集合操作
我运行了2个测试,执行以下语句:
setList:列表。设置(0,价值); setArray:数组[0]=值;
结果(以纳秒为单位):
a.p.g.a.ArrayVsList.setArray [4.201, 4.236]
a.p.g.a.ArrayVsList.setList [6.783, 6.877]
结论:在数组上的set操作比在列表上快40%左右,但是,对于get,每个set操作需要几纳秒——所以为了达到1秒的差异,需要在列表/数组中设置项数亿次!
无性系/ copy
ArrayList的复制构造函数委托给数组。因此,性能与数组复制相同(通过克隆复制数组,数组。copyOf或System。arrayCopy在性能方面没有实质性的差异)。
既然这里已经有了很多好的答案,我想给你一些其他的实际观点的信息,这是插入和迭代性能的比较:Java中的基元数组与链表。
这是实际的简单性能检查。因此,结果将取决于机器的性能。
用于此的源代码如下:
import java.util.Iterator;
import java.util.LinkedList;
public class Array_vs_LinkedList {
private final static int MAX_SIZE = 40000000;
public static void main(String[] args) {
LinkedList lList = new LinkedList();
/* insertion performance check */
long startTime = System.currentTimeMillis();
for (int i=0; i<MAX_SIZE; i++) {
lList.add(i);
}
long stopTime = System.currentTimeMillis();
long elapsedTime = stopTime - startTime;
System.out.println("[Insert]LinkedList insert operation with " + MAX_SIZE + " number of integer elapsed time is " + elapsedTime + " millisecond.");
int[] arr = new int[MAX_SIZE];
startTime = System.currentTimeMillis();
for(int i=0; i<MAX_SIZE; i++){
arr[i] = i;
}
stopTime = System.currentTimeMillis();
elapsedTime = stopTime - startTime;
System.out.println("[Insert]Array Insert operation with " + MAX_SIZE + " number of integer elapsed time is " + elapsedTime + " millisecond.");
/* iteration performance check */
startTime = System.currentTimeMillis();
Iterator itr = lList.iterator();
while(itr.hasNext()) {
itr.next();
// System.out.println("Linked list running : " + itr.next());
}
stopTime = System.currentTimeMillis();
elapsedTime = stopTime - startTime;
System.out.println("[Loop]LinkedList iteration with " + MAX_SIZE + " number of integer elapsed time is " + elapsedTime + " millisecond.");
startTime = System.currentTimeMillis();
int t = 0;
for (int i=0; i < MAX_SIZE; i++) {
t = arr[i];
// System.out.println("array running : " + i);
}
stopTime = System.currentTimeMillis();
elapsedTime = stopTime - startTime;
System.out.println("[Loop]Array iteration with " + MAX_SIZE + " number of integer elapsed time is " + elapsedTime + " millisecond.");
}
}
表现结果如下:
数组更快-所有内存都是预先分配的。
您应该更喜欢泛型类型而不是数组。正如其他人所提到的,数组是不灵活的,不具有泛型类型的表达能力。(它们确实支持运行时类型检查,但这与泛型类型混在一起很糟糕。)
但是,与往常一样,在优化时,你应该始终遵循以下步骤:
Don't optimize until you have a nice, clean, and working version of your code. Changing to generic types could very well be motivated at this step already. When you have a version that is nice and clean, decide if it is fast enough. If it isn't fast enough, measure its performance. This step is important for two reasons. If you don't measure you won't (1) know the impact of any optimizations you make and (2) know where to optimize. Optimize the hottest part of your code. Measure again. This is just as important as measuring before. If the optimization didn't improve things, revert it. Remember, the code without the optimization was clean, nice, and working.