我必须在内存中保留数千个字符串,以便在Java中串行访问。我应该把它们存储在数组中还是应该使用某种列表?
由于数组将所有数据保存在一个连续的内存块中(与list不同),使用数组存储数千个字符串会导致问题吗?
我必须在内存中保留数千个字符串,以便在Java中串行访问。我应该把它们存储在数组中还是应该使用某种列表?
由于数组将所有数据保存在一个连续的内存块中(与list不同),使用数组存储数千个字符串会导致问题吗?
当前回答
数组更快-所有内存都是预先分配的。
其他回答
数组列表在内部使用数组对象来添加(或存储)对象 元素。换句话说,ArrayList由Array数据支持 结构。ArrayList的数组是可调整大小的(或动态的)。
Array比ArrayList快,因为ArrayList内部使用数组。如果我们可以直接在数组中添加元素,而间接地在数组中添加元素 数组通过数组列表总是直接机制比间接机制快。
在ArrayList类中有两个重载的add()方法:
add(Object):将一个对象添加到列表末尾。 add(int index, Object):将指定对象插入到列表的指定位置。
数组列表的大小如何动态增长?
public boolean add(E e)
{
ensureCapacity(size+1);
elementData[size++] = e;
return true;
}
An important point to note from the above code is that we are checking the capacity of the ArrayList, before adding the element. ensureCapacity() determines what is the current size of occupied elements and what is the maximum size of the array. If the size of the filled elements (including the new element to be added to the ArrayList class) is greater than the maximum size of the array then increase the size of the array. But the size of the array can not be increased dynamically. So what happens internally is new Array is created with the capacity
到 Java 6
int newCapacity = (oldCapacity * 3)/2 + 1;
(更新)来自Java 7
int newCapacity = oldCapacity + (oldCapacity >> 1);
此外,旧数组中的数据被复制到新数组中。
数组列表中有开销方法这就是为什么数组比数组列表快。
这取决于你如何访问它。
存储后,如果你主要想做搜索操作,很少或不需要插入/删除,那么就去数组(因为在数组中搜索是在O(1)中完成的,而添加/删除可能需要重新排序元素)。
存储之后,如果你的主要目的是添加/删除字符串,很少或没有搜索操作,那么就去List。
我猜最初的海报来自c++ /STL背景,这引起了一些混乱。在c++中std::list是一个双链表。
在Java中[Java .util]。List是一个不需要实现的接口(c++术语中的纯抽象类)。List可以是一个双重链表——提供了java.util.LinkedList。然而,100次中有99次,当你想要创建一个新的List时,你想要使用java.util.ArrayList来代替,这是c++ std::vector的大致等价。还有其他标准实现,比如java.util.Collections.emptyList()和java.util.Arrays.asList()返回的那些。
从性能的角度来看,不得不通过一个接口和一个额外的对象会有很小的影响,但是运行时内联意味着这很少有任何意义。还要记住String通常是一个对象加数组。所以对于每个元素,你可能有两个其他的对象。在c++ std::vector<std::string>中,虽然按值复制而不使用指针,但字符数组将形成一个string对象(通常不会共享这些对象)。
如果这段代码对性能非常敏感,那么可以为所有字符串的所有字符创建一个char[]数组(甚至byte[]),然后创建一个偏移量数组。IIRC,这是javac的实现方式。
没有一个答案有我感兴趣的信息——重复扫描同一个数组很多很多次。必须为此做一个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);
}
}
我来这里是为了更好地感受使用列表而不是数组对性能的影响。我不得不为我的场景调整代码:数组/列表的~1000个整型,主要使用getter,即数组[j] vs. list.get(j)
从7个中选择最好的并不科学(前几个列表的速度慢2.5倍),我得到了这样的结果:
array Integer[] best 643ms iterator
ArrayList<Integer> best 1014ms iterator
array Integer[] best 635ms getter
ArrayList<Integer> best 891ms getter (strange though)
用数组大约快30%
现在发表文章的第二个原因是,没有人会提到使用嵌套循环编写数学/矩阵/模拟/优化代码的影响。
假设你有三个嵌套层,而内部循环的速度是原来的两倍,那么你的性能就会下降8倍。一天就能完成的事情现在需要一个星期。
*编辑 这里非常震惊,我试图声明int[1000]而不是Integer[1000]
array int[] best 299ms iterator
array int[] best 296ms getter
使用Integer[] vs. int[]表示双倍的性能打击,带有迭代器的ListArray比int[]慢3倍。真的认为Java的列表实现类似于本机数组…
参考代码(多次调用):
public static void testArray()
{
final long MAX_ITERATIONS = 1000000;
final int MAX_LENGTH = 1000;
Random r = new Random();
//Integer[] array = new Integer[MAX_LENGTH];
int[] array = new int[MAX_LENGTH];
List<Integer> list = new ArrayList<Integer>()
{{
for (int i = 0; i < MAX_LENGTH; ++i)
{
int val = r.nextInt();
add(val);
array[i] = val;
}
}};
long start = System.currentTimeMillis();
int test_sum = 0;
for (int i = 0; i < MAX_ITERATIONS; ++i)
{
// for (int e : array)
// for (int e : list)
for (int j = 0; j < MAX_LENGTH; ++j)
{
int e = array[j];
// int e = list.get(j);
test_sum += e;
}
}
long stop = System.currentTimeMillis();
long ms = (stop - start);
System.out.println("Time: " + ms);
}