我必须在内存中保留数千个字符串,以便在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);
    }

}

其他回答

数组列表在内部使用数组对象来添加(或存储)对象 元素。换句话说,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);

此外,旧数组中的数据被复制到新数组中。

数组列表中有开销方法这就是为什么数组比数组列表快。

如果你可以使用固定的大小,数组将会更快,需要更少的内存。

如果您需要List接口在添加和删除元素方面的灵活性,那么问题仍然是应该选择哪种实现。通常在任何情况下都推荐使用ArrayList,但如果必须删除或插入列表开头或中间的元素,ArrayList也有其性能问题。

因此,您可能想要看看https://dzone.com/articles/gaplist-lightning-fast-list,它介绍了GapList。这个新的列表实现结合了ArrayList和LinkedList的优点,使得几乎所有的操作都有很好的性能。请登录https://github.com/magicwerk/brownies-collections获取。

不要在没有适当基准测试的情况下陷入优化的陷阱。正如其他人建议的那样,在做出任何假设之前使用分析器。

您所列举的不同数据结构具有不同的用途。列表在开头和结尾插入元素时非常有效,但在访问随机元素时却很困难。数组具有固定的存储,但提供快速的随机访问。最后,ArrayList通过允许数组增长来改进与数组的接口。通常,要使用的数据结构应该由如何访问或添加存储的数据来决定。

About memory consumption. You seem to be mixing some things. An array will only give you a continuous chunk of memory for the type of data that you have. Don't forget that java has a fixed data types: boolean, char, int, long, float and Object (this include all objects, even an array is an Object). It means that if you declare an array of String strings [1000] or MyObject myObjects [1000] you only get a 1000 memory boxes big enough to store the location (references or pointers) of the objects. You don't get a 1000 memory boxes big enough to fit the size of the objects. Don't forget that your objects are first created with "new". This is when the memory allocation is done and later a reference (their memory address) is stored in the array. The object doesn't get copied into the array only it's reference.

如果提前知道数据有多大,那么使用数组会更快。

List更加灵活。你可以使用由数组支持的数组列表。

数组建议你在任何地方使用它们而不是列表,特别是在你知道项目的数量和大小不会改变的情况下。

参见Oracle Java最佳实践:http://docs.oracle.com/cd/A97688_16/generic.903/bp/java.htm#1007056

当然,如果需要多次从集合中添加和删除对象,则使用简单列表。