Java中的数组或列表。哪个更快?

时间:2009-04-04 05:57:28

标签: java arrays performance list containers

我必须在内存中保留数千个字符串,以便在Java中以串行方式访问。我应该将它们存储在数组中还是应该使用某种List?

由于数组将所有数据保存在连续的内存块中(与Lists不同),使用数组来存储数千个字符串会导致问题吗?

32 个答案:

答案 0 :(得分:344)

我建议您使用分析器来测试哪个更快。

我个人认为你应该使用列表。

我在大型代码库上工作,而前一组开发人员使用无处不在的数组。它使代码非常不灵活。在将大块文件更改为列表后,我们注意到速度没有差异。

答案 1 :(得分:157)

Java方式是您应该考虑哪些数据抽象最适合您的需求。请记住,在Java中,List是一个抽象,而不是具体的数据类型。您应该将字符串声明为List,然后使用ArrayList实现初始化它。

List<String> strings = new ArrayList<String>();

抽象数据类型和具体实现的这种分离是面向对象编程的关键方面之一。

ArrayList使用数组作为其底层实现来实现List Abstract Data Type。访问速度几乎与数组相同,还有一个额外的优点:能够向List添加和减去元素(虽然这是一个带有ArrayList的O(n)操作),如果您决定稍后更改底层实现您可以。例如,如果您意识到需要同步访问,则可以将实现更改为Vector,而无需重写所有代码。

实际上,ArrayList是专门为在大多数情况下替换低级数组构造而设计的。如果今天设计Java,那么完全有可能完全忽略数组以支持ArrayList结构。

  

由于数组将所有数据保存在连续的内存块中(与Lists不同),使用数组来存储数千个字符串会导致问题吗?

在Java中,所有集合仅存储对象的引用,而不存储对象本身。数组和ArrayList都会在连续数组中存储几千个引用,因此它们基本相同。您可以考虑在现代硬件上始终可以使用几千个32位引用的连续块。这并不能保证你不会完全耗尽内存,当然,只是内存需求的连续块并不难实现。

答案 2 :(得分:92)

您应该优先选择通用类型而不是数组。正如其他人所提到的,数组是不灵活的,并且没有泛型类型的表达能力。 (但它们确实支持运行时类型检查,但是它与泛型类型混合得非常糟糕。)

但是,与往常一样,在优化时,您应始终遵循以下步骤:

  • 在您拥有一个漂亮,干净且正常工作版本的代码之前,请不要进行优化。在这一步骤中,可以很好地改变通用类型。
  • 当你的版本很干净时,请确定它是否足够快。
  • 如果速度不够快,衡量其效果。这一步很重要,原因有两个。如果你不衡量,你将不会(1)知道你做出的任何优化的影响,(2)知道在哪里进行优化。
  • 优化代码中最热门的部分。
  • 再次测量。这与以前测量一样重要。如果优化没有改善,还原。请记住,没有<​​/ em>优化的代码 干净,漂亮且有效。

答案 3 :(得分:86)

尽管建议使用ArrayList的答案在大多数情况下都有意义,但实际的相对性能问题还没有真正得到解答。

您可以使用数组做一些事情:

  • 创建
  • 设置项目
  • 获取项目
  • 克隆/复制

总结论

尽管在ArrayList 上的get和set操作稍慢(在我的机器上每次调用分别为1和3纳秒),使用ArrayList与使用ArrayList的开销非常小任何非密集使用的数组。但有一些事情要记住:

  • 调整列表上的操作(调用list.add(...)时)是非常昂贵的,并且应尽可能尝试将初始容量设置在适当的水平(请注意,使用数组时会出现同样的问题)
  • 在处理基元时,数组可以明显更快,因为它们可以避免许多装箱/拆箱转换
  • 只在ArrayList中获取/设置值的应用程序(不常见!)通过切换到数组可以看到性能提升超过25%

详细结果

以下是我在标准x86桌面计算机上使用JDK 7使用jmh benchmarking library(纳秒时间)测量这三个操作的结果。请注意,ArrayList在测试中从不调整大小以确保结果具有可比性。 Benchmark code available here

Array / ArrayList Creation

我运行了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]

结论:没有明显差异

获取操作

我运行了2个测试,执行以下语句:

  • getList:return list.get(0);
  • getArray:return array[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:list.set(0, value);
  • setArray:array[0] = value;

结果(每次通话以纳秒为单位):

a.p.g.a.ArrayVsList.setArray   [4.201, 4.236]
a.p.g.a.ArrayVsList.setList    [6.783, 6.877]

结论:对数组的集合操作比列表更快,但是,对于get,每个集合操作需要几纳秒 - 因此差异达到1秒,需要在列表/数组中设置数亿次!

克隆/复制

ArrayList的复制构造函数委托给Arrays.copyOf,因此性能与数组复制相同(通过cloneArrays.copyOfSystem.arrayCopy makes no material difference performance-wise复制数组。 / p>

答案 4 :(得分:22)

我猜测原始海报来自C ++ / STL背景,这引起了一些混乱。在C ++中,std::list是一个双向链表。

在Java中[java.util.]List是一个无实现的接口(C ++术语中的纯抽象类)。 List可以是双重链接列表 - 提供java.util.LinkedList。但是,如果您想要创建一个新的List,则需要使用java.util.ArrayList的99次,这是C ++ std::vector的粗略等价物。还有其他标准实现,例如java.util.Collections.emptyList()java.util.Arrays.asList()返回的实现。

从性能的角度来看,不得不通过一个接口和一个额外的对象,但是运行时内联意味着这很少有任何意义。还要记住String通常是一个对象加数组。因此,对于每个条目,您可能还有另外两个对象。在C ++ std::vector<std::string>中,尽管没有指针按值复制,但字符数组将形成字符串的对象(通常不会共享这些对象)。

如果此特定代码对性能非常敏感,则可以为所有字符串的所有字符创建单个char[]数组(甚至byte[]),然后创建一个偏移数组。 IIRC,这就是javac的实施方式。

答案 5 :(得分:13)

我同意在大多数情况下,您应该选择ArrayLists相对于阵列的灵活性和优雅性 - 在大多数情况下,对程序性能的影响可以忽略不计。

但是,如果你在软件图形渲染或自定义虚拟机上进行持续的重复迭代而几乎没有结构变化(没有添加和删除),我的顺序访问基准测试显示 ArrayLists是1.5在我的系统上x比数组慢[在我一岁的iMac上使用Java 1.6。)

一些代码:

import java.util.*;

public class ArrayVsArrayList {
    static public void main( String[] args ) {

        String[] array = new String[300];
        ArrayList<String> list = new ArrayList<String>(300);

        for (int i=0; i<300; ++i) {
            if (Math.random() > 0.5) {
                array[i] = "abc";
            } else {
                array[i] = "xyz";
            }

            list.add( array[i] );
        }

        int iterations = 100000000;
        long start_ms;
        int sum;

        start_ms = System.currentTimeMillis();
        sum = 0;

        for (int i=0; i<iterations; ++i) {
          for (int j=0; j<300; ++j) sum += array[j].length();
        }

        System.out.println( (System.currentTimeMillis() - start_ms) + " ms (array)" );
        // Prints ~13,500 ms on my system

        start_ms = System.currentTimeMillis();
        sum = 0;

        for (int i=0; i<iterations; ++i) {
          for (int j=0; j<300; ++j) sum += list.get(j).length();
        }

        System.out.println( (System.currentTimeMillis() - start_ms) + " ms (ArrayList)" );
        // Prints ~20,800 ms on my system - about 1.5x slower than direct array access
    }
}

答案 6 :(得分:11)

我写了一个小基准来比较ArrayLists和Arrays。在我的旧式笔记本电脑上,遍历5000个元素的arraylist 1000次的时间比等效的数组代码慢大约10毫秒。

所以,如果你只是在迭代列表,而你正在做很多事情,那么可能值得进行优化。否则我会使用List,因为当 需要优化代码时,它会更容易。

n.b。我 注意到使用for String s: stringsList比使用旧式for循环访问列表慢约50%。去图......这是我定时的两个功能;数组和列表中填充了5000个随机(不同)字符串。

private static void readArray(String[] strings) {
    long totalchars = 0;
    for (int j = 0; j < ITERATIONS; j++) {
        totalchars = 0;
        for (int i = 0; i < strings.length; i++) {
            totalchars += strings[i].length();

        }
    }
}

private static void readArrayList(List<String> stringsList) {
    long totalchars = 0;
    for (int j = 0; j < ITERATIONS; j++) {
        totalchars = 0;
        for (int i = 0; i < stringsList.size(); i++) {
            totalchars += stringsList.get(i).length();
        }
    }
}

答案 7 :(得分:11)

首先,值得澄清一下你是说“经典comp sci数据结构意义上的”列表(即链表)还是指java.util.List?如果你的意思是java.util.List,那就是一个接口。如果你想使用数组,只需使用ArrayList实现,你将获得类似数组的行为和语义。问题解决了。

如果你的意思是一个数组与一个链表,那就是我们回到Big O的一个稍微不同的参数(如果这是一个不熟悉的术语,这里是plain English explanation

阵列;

  • 随机访问:O(1);
  • 插入:O(n);
  • 删除:O(n)。

链接列表:

  • 随机访问:O(n);
  • 插入:O(1);
  • 删除:O(1)。

因此,您可以选择最适合调整数组大小的方法。如果您调整大小,插入和删除很多,那么链接列表可能是更好的选择。如果随机访问很少,则同样如此。你提到串行访问。如果你主要是通过很少的修改来进行串行访问,那么你选择哪个并不重要。

链接列表的开销略高,因为就像你说的那样,你正在处理潜在的非连续内存块和(有效地)指向下一个元素的指针。除非你处理数百万条款,否则这可能不是一个重要的因素。

答案 8 :(得分:6)

不,因为从技术上讲,数组只存储对字符串的引用。字符串本身分配在不同的位置。对于一千个项目,我会说一个列表会更好,它更慢,但它提供了更大的灵活性,并且更容易使用,特别是如果你要调整它们的大小。

答案 9 :(得分:5)

如果您有数千人,请考虑使用trie。 trie是一种树状结构,它合并了存储字符串的公共前缀。

例如,如果字符串是

intern
international
internationalize
internet
internets

特里会存储:

intern
 -> \0
 international
 -> \0
 -> ize\0
 net
 ->\0
 ->s\0

字符串需要57个字符(包括空终止符'\ 0')来存储,加上包含它们的String对象的大小。 (事实上​​,我们应该把所有大小都调整到16的倍数,但是......)称之为57 + 5 = 62字节,粗略。

trie需要29(包括空终止符,'\ 0')进行存储,加上trie节点的大小,它们是数组的引用和子节点的节点列表。

对于这个例子,可能出现的情况大致相同;对于成千上万的人来说,只要你有共同的前缀,它就可能会少出来。

现在,在其他代码中使用trie时,您必须转换为String,可能使用StringBuffer作为中介。如果许多字符串同时作为字符串使用,在特里结束之外,这是一种损失。

但如果你当时只使用一些 - 比如说,在字典中查找东西 - 这个特里可以为你节省很多空间。绝对比将它们存储在HashSet中的空间要小。

你说你是“连续地”访问它们 - 如果这意味着按字母顺序依次访问它们,那么如果你以深度优先的方式迭代它,trie显然也会免费提供字母顺序。

答案 10 :(得分:5)

由于这里已经有很多好的答案,我想提供一些实用视图的其他信息,即插入和迭代性能比较: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.");
    }
}

绩效结果如下:

enter image description here

答案 11 :(得分:5)

更新:

正如Mark所说,在JVM预热(几次测试通过)之后没有显着差异。检查重新创建的数组甚至是新行矩阵开始的新传递。很有可能这标志着索引访问的简单数组不能用于集合。

仍然是前1-2次传递简单阵列的速度要快2-3倍。

原始邮件:

太多的单词对于主题太简单无法检查。 没有任何问题数组比任何类容器快几倍。我运行这个问题寻找我的性能关键部分的替代品。这是我为检查实际情况而构建的原型代码:

import java.util.List;
import java.util.Arrays;

public class IterationTest {

    private static final long MAX_ITERATIONS = 1000000000;

    public static void main(String [] args) {

        Integer [] array = {1, 5, 3, 5};
        List<Integer> list = Arrays.asList(array);

        long start = System.currentTimeMillis();
        int test_sum = 0;
        for (int i = 0; i < MAX_ITERATIONS; ++i) {
//            for (int e : array) {
            for (int e : list) {
                test_sum += e;
            }
        }
        long stop = System.currentTimeMillis();

        long ms = (stop - start);
        System.out.println("Time: " + ms);
    }
}

以下是答案:

基于数组(第16行有效):

Time: 7064

基于列表(第17行有效):

Time: 20950

对“更快”的评论?这是很清楚的。问题是当你比List的灵活性更快3倍的时候。但这是另一个问题。 顺便说一句,我也是根据手工构建的ArrayList来检查这个。几乎相同的结果。

答案 12 :(得分:4)

在存储字符串对象的情况下,数组与列表选择并不那么重要(考虑性能)。因为数组和列表都将存储字符串对象引用,而不是实际对象。

  1. 如果字符串数几乎不变,那么使用数组(或ArrayList)。但如果数字变化太大,那么你最好使用LinkedList。
  2. 如果有(或将来)需要在中间添加或删除元素,那么你当然必须使用LinkedList。

答案 13 :(得分:4)

请记住,ArrayList封装了一个数组,因此与使用原始数组相比几乎没有什么区别(除了在Java中使用List更容易处理的事实之外)。

在您存储基元(即byte,int等)时,将数组放入ArrayList几乎是唯一有意义的时候,您需要使用原始数组获得特定的空间效率。

答案 14 :(得分:3)

如果事先知道数据有多大,那么数组会更快。

列表更灵活。您可以使用由数组支持的ArrayList。

答案 15 :(得分:3)

它可以使用固定大小,数组将更快,并且需要更少的内存。

如果您需要使用添加和删除元素的List界面的灵活性,问题仍然是您应该选择哪种实现。建议使用ArrayList以用于任何情况,但如果必须删除或插入列表开头或中间的元素,ArrayList也会出现性能问题。

因此,您可能需要查看引入GapList的http://java.dzone.com/articles/gaplist-%E2%80%93-lightning-fast-list。这个新的列表实现结合了ArrayList和LinkedList的优势,几乎可以为所有操作提供非常好的性能。

答案 16 :(得分:3)

列表比数组慢。如果你需要效率使用数组。如果你需要灵活性使用列表。

答案 17 :(得分:2)

取决于实施。原始类型数组可能比ArrayList更小,更有效。这是因为数组将直接将值存储在连续的内存块中,而最简单的ArrayList实现将存储指向每个值的指针。特别是在64位平台上,这可以产生巨大的差异。

当然,对于这种情况,jvm实现可能有一个特殊情况,在这种情况下性能将是相同的。

答案 18 :(得分:2)

“千人”并不是一个很大的数字。几千个段落长度的字符串大小为几兆字节。如果您只想连续访问这些内容,请使用an immutable singly-linked List

答案 19 :(得分:2)

我来这里的目的是更好地了解在数组上使用列表对性能的影响。我必须在这里针对我的情况调整代码:数组/列表(约1000个整数)主要使用吸气剂,这意味着array [j]与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 []与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);
    }

答案 20 :(得分:2)

List是java 1.5及更高版本中的首选方式,因为它可以使用泛型。数组不能有泛型。此外,数组具有预定义的长度,不能动态增长。初始化大尺寸的数组不是一个好主意。 ArrayList是使用泛型声明数组的方法,它可以动态增长。 但是如果更频繁地使用delete和insert,那么链表就是要使用的最快的数据结构。

答案 21 :(得分:2)

建议在任何地方使用阵列而不是列表,尤其是如果您知道项目数量和大小不会改变的话。

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

当然,如果您需要多次添加和删除集合中的对象,请轻松使用列表。

答案 22 :(得分:2)

没有一个答案有我感兴趣的信息 - 多次重复扫描同一阵列。不得不为此创建一个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

<强>测试

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);
    }

}

答案 23 :(得分:1)

数组更快 - 所有内存都是事先预先分配的。

答案 24 :(得分:1)

我认为它对Strings没有什么影响。字符串数组中的连续内容是对字符串的引用,字符串本身存储在内存中的随机位置。

阵列与列表可以对基本类型产生影响,而不是对象。 IF 你事先知道元素的数量,并且不需要灵活性,数百万的整数或双精度数组在内存和速度方面比列表更有效,因为它们确实会连续存储并立即访问。这就是为什么Java仍然使用字符串数组作为字符串,图像数据的整数数组等等。

答案 25 :(得分:1)

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

您枚举的不同数据结构具有不同的用途。列表在开头和结尾插入元素非常有效,但在访问随机元素时会遇到很多问题。阵列具有固定存储但提供快速随机访问。最后,ArrayList通过允许它增长来改进数组的接口。通常,要使用的数据结构应取决于存储数据的访问或添加方式。

关于内存消耗。你似乎在混合一些东西。数组只会为您拥有的数据类型提供连续的内存块。不要忘记java有一个固定的数据类型:boolean,char,int,long,float和Object(这包括所有对象,甚至数组都是Object)。这意味着如果您声明一个String字符串[1000]或MyObject myObjects [1000]的数组,您只能获得足够大的1000个内存框来存储对象的位置(引用或指针)。你没有足够大的1000个内存盒来适应对象的大小。不要忘记您的对象首先使用“new”创建。这是在完成内存分配并稍后将引用(它们的内存地址)存储在数组中的时候。只有它的引用才会将对象复制到数组中。

答案 26 :(得分:1)

这里给出的许多微基准测试发现像array / ArrayList读取这样的数字只有几纳秒。如果所有内容都在您的L1缓存中,这是非常合理的。

更高级别的高速缓存或主存储器访问可以具有10nS-100nS之类的数量级,而更像是L1高速缓存的1nS。访问ArrayList有一个额外的内存间接,在实际应用程序中,您可以从几乎从不到每次都支付这笔费用,具体取决于您的代码在访问之间的操作。而且,当然,如果你有很多小的ArrayLists,这可能会增加你的内存使用量,并使你更有可能遇到缓存未命中。

原始海报似乎只使用一张并在短时间内访问了很多内容,所以应该没有太大的困难。但是对于其他人来说可能会有所不同,你应该在解释微基准时注意。

然而,Java Strings非常浪费,特别是如果你存储了很多小的(只需用内存分析器查看它们,对于一些字符串来说似乎是> 60字节)。字符串数组与String对象间接,另一个字符串从String对象到包含字符串本身的char []。如果有什么东西会打破你的L1缓存,那就是成千上万或几万个字符串。所以,如果你认真 - 非常严肃 - 关于尽可能多地削减性能,那么你可以用不同的方式来做。比方说,您可以拥有两个数组,一个包含所有字符串的char [],一个接一个,以及一个带有启动偏移量的int []。这将是一个PITA做任何事情,你几乎肯定不需要它。如果你这样做,你就选择了错误的语言。

答案 27 :(得分:0)

使用哪个取决于问题。我们需要看看大O。

Big O Values of List and Array

图片来源:https://github.com/egonSchiele/grokking_algorithms

答案 28 :(得分:0)

数组 - 当我们必须更快地获取结果时总会更好

列表 - 在插入和删除时执行结果,因为它们可以在O(1)中完成,这也提供了轻松添加,获取和删除数据的方法。更容易使用。

但是请记住,当存储数据的数组中的索引位置时,获取数据会很快。

这可以通过对数组进行排序来实现。因此,这增加了获取数据的时间(即,存储数据+对数据进行排序+寻找找到数据的位置)。因此,这增加了从阵列获取数据的额外延迟,即使他们可能更擅长更快地获取数据。

因此,这可以通过trie数据结构或三元数据结构来解决。如上所述,特里数据结构在搜索数据时非常有效,可以以O(1)幅度完成对特定字的搜索。当时间问题,即;如果您必须快速搜索和检索数据,您可以使用trie数据结构。

如果您希望减少内存空间并希望获得更好的性能,那么请使用三元数据结构。这两者都适合存储大量的字符串(例如,像字典中包含的单词)。

答案 29 :(得分:0)

  

ArrayList在内部使用数组对象来添加(或存储)   元素。换句话说,ArrayList由Array数据支持   -structure.The ArrayList的数组是可调整大小的(或动态的)。

数组比数组快,因为ArrayList内部使用数组。如果我们可以直接在Array中添加元素并间接添加元素 Array通过ArrayList总是直接机制比间接机制更快。

ArrayList类中有两个重载的add()方法:
1. add(Object):将对象添加到列表的末尾 2. add(int index , Object ):将指定的对象插入列表中的指定位置。

ArrayList的大小如何动态增长?

public boolean add(E e)        
{       
     ensureCapacity(size+1);
     elementData[size++] = e;         
     return true;
}

从上面的代码中注意到的重点是我们在添加元素之前检查ArrayList的容量。 ensureCapacity()确定占用元素的当前大小以及数组的最大大小。如果填充元素的大小(包括要添加到ArrayList类的新元素)大于数组的最大大小,则增加数组的大小。但是数组的大小不能动态增加。那么内部发生的是新的数据是用容量

创建的

直到Java 6

int newCapacity = (oldCapacity * 3)/2 + 1;

(更新)来自Java 7

 int newCapacity = oldCapacity + (oldCapacity >> 1);

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

在ArrayList中使用开销方法,这就是Array比ArrayList更快的原因。

答案 30 :(得分:0)

这取决于您如何访问它。

存储之后,如果你主要想做搜索操作,很少或没有插入/删除,那么转到数组(因为搜索在数组中的O(1)中完成,而添加/删除可能需要重新排序元素)。

存储后,如果您的主要目的是添加/删除字符串,只需很少或不需要搜索操作,那么请转到列表。

答案 31 :(得分:-3)

List更灵活....所以List比数组更好