代码之家  ›  专栏  ›  技术社区  ›  euphoria83

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

  •  324
  • euphoria83  · 技术社区  · 15 年前

    我必须在内存中保留数以千计的字符串,以便在Java中连续访问。我应该将它们存储在数组中还是使用某种列表?

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

    32 回复  |  直到 6 年前
        1
  •  338
  •   Fortyrunner    15 年前

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

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

    我在一个大型代码库和以前的开发人员使用的数组组上工作 到处 .这使得代码非常不灵活。在把它的大块更改为列表之后,我们注意到速度上没有差别。

        2
  •  154
  •   cygil    15 年前

    Java方式是你应该考虑什么数据 抽象化 最适合你的需求。请记住,在Java中,列表是抽象的,不是具体的数据类型。您应该将字符串声明为列表,然后使用arraylist实现对其进行初始化。

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

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

    ArrayList使用数组作为其基础实现来实现列表抽象数据类型。访问速度实际上与数组相同,它还有另外的优点,即能够向列表中添加和减去元素(尽管这是一个带array list的O(N)操作),如果您决定稍后更改底层实现,则可以。例如,如果您意识到需要同步访问,您可以将实现更改为矢量,而无需重写所有代码。

    事实上,arraylist是专门为在大多数上下文中替换低级数组构造而设计的。如果Java现在被设计,那么完全有可能完全忽略数组来支持ARARYLIST结构。

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

    在Java中,所有集合只存储对对象的引用,而不存储对象本身。数组和arraylist都将在一个连续的数组中存储几千个引用,因此它们本质上是相同的。您可以考虑,在现代硬件上,由几千个32位引用组成的连续块总是随时可用的。当然,这并不能保证您不会完全耗尽内存,只是连续的内存块需求对fufil来说并不困难。

        3
  •  92
  •   Kelmikra    9 年前

    您应该更喜欢泛型类型而不是数组。正如其他人所提到的,数组是不灵活的,没有泛型类型的表达能力。(不过,它们确实支持运行时类型检查,但与一般类型的混合很糟糕。)

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

    • 不要优化,直到你有一个好的,干净的,和 工作 代码的版本。在这一步中,很可能已经开始改变通用类型。
    • 当你有一个干净漂亮的版本时,决定它是否足够快。
    • 如果不够快, 测量其性能 .这一步很重要,有两个原因。如果你不度量,你将无法(1)知道你所做的任何优化的影响,(2)知道在哪里进行优化。
    • 优化代码中最热门的部分。
    • 再次测量。 这和以前的测量一样重要。如果优化没有改善, 恢复它 . 记住,密码 没有 优化是 干净,漂亮,工作。
        4
  •  82
  •   Community noseratio    7 年前

    虽然建议使用arraylist的答案在大多数情况下都是有意义的,但是实际的相对性能问题并没有得到真正的回答。

    使用数组可以执行以下操作:

    • 创造它
    • 设置一个项目
    • 得到一个项目
    • 克隆/复制

    一般结论

    尽管get和set操作在arraylist上稍微慢一些 (RESP)在我的机器上每次呼叫1和3纳秒, 对于任何非密集使用,使用arraylist和数组的开销都很小。 然而,有一些事情需要牢记:

    • 调整列表上的操作大小(调用 list.add(...) )成本高昂,应尽可能将初始容量设置在适当的水平(请注意,使用阵列时也会出现同样的问题)
    • 在处理基元时,数组可以更快,因为它们允许一个数组避免许多装箱/拆箱转换。
    • 只在数组列表中获取/设置值的应用程序(不太常见!)通过切换到阵列可以看到超过25%的性能增益

    详细结果

    下面是我使用 jmh benchmarking library (以纳秒为单位)在标准x86台式机上使用JDK7。注意,在测试中,arraylist永远不会调整大小以确保结果具有可比性。 Benchmark code available here .

    数组/数组列表创建

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

    • 创建一个: Integer[] array = new Integer[1];
    • Curtelelist1: List<Integer> list = new ArrayList<> (1);
    • 创建数组10000: Integer[] array = new Integer[10000];
    • 创建列表10000: 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);
    • 获取数组: return array[0];

    结果(以纳秒为单位,每次调用95%置信度):

    a.p.g.a.ArrayVsList.getArray   [2.958, 2.984]
    a.p.g.a.ArrayVsList.getList    [3.841, 3.874]
    

    结论:从阵列中获取数据的速度大约快25%。 而不是从阵列列表中获取,尽管差异仅在一纳秒的阶数上。

    集合运算

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

    • 设置列表: list.set(0, value);
    • 设置数组: array[0] = value;

    结果(以纳秒为单位,每次调用):

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

    结论:阵列上的集合运算速度大约快40% 但是,对于get,每个set操作都需要几纳秒的时间-因此,如果差异达到1秒,则需要在列表/数组中设置数亿次的项!

    克隆/复制

    arraylist的复制构造函数委托给 Arrays.copyOf 因此,性能与阵列复制相同(通过 clone , 复制品 System.arrayCopy makes no material difference performance-wise )

        5
  •  22
  •   Tom Hawtin - tackline    9 年前

    我猜原来的海报是来自一个C++/STL背景,这造成一些混乱。在C++中 std::list 是双链接列表。

    在爪哇 [java.util.]List 是一个实现自由接口(C++抽象纯抽象类)。 List 可以是双重链接列表- java.util.LinkedList 提供。但是,当你想要一个新的 ,您要使用 java.util.ArrayList 相反,它是C++的粗糙等价物。 std::vector . 还有其他标准实现,例如 java.util.Collections.emptyList() java.util.Arrays.asList() .

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

    如果此特定代码确实对性能敏感,则可以创建一个 char[] 数组(或偶数) byte[] )对于所有字符串的所有字符,然后是偏移数组。IIRC,这就是javac的实现方式。

        6
  •  12
  •   Wrench    7 年前

    我同意在大多数情况下,您应该选择数组列表的灵活性和优雅性,而在大多数情况下,对程序性能的影响是可以忽略的。

    但是,如果您在对软件图形渲染或自定义虚拟机进行持续的、繁重的迭代,并且几乎没有结构更改(没有添加和删除),那么我的顺序访问基准测试表明 arraylist比数组慢1.5倍 在我的系统上(Java 1.6在我一岁的iMac上)。

    一些代码:

    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
        }
    }
    
        7
  •  11
  •   Community noseratio    7 年前

    首先,值得澄清的是,在经典的comp-sci数据结构意义上,你的意思是“list”(链表)还是java.util.list?如果您的意思是java.util.list,那么它是一个接口。如果您想使用数组,只需使用arraylist实现,就可以得到类似数组的行为和语义。问题解决了。

    如果你是说一个数组和一个链表,这是一个稍有不同的参数,我们回到大O(这里是一个 plain English explanation 如果这是一个不熟悉的术语。

    数组;

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

    链表:

    • 随机接入:O(N);
    • 插入:O(1);
    • 删除:O(1)。

    所以您可以选择最适合您调整数组大小的方法。如果调整大小、插入和删除很多,那么链接列表可能是更好的选择。如果随机访问很少,情况也是如此。你提到串行访问。如果您主要进行串行访问,修改很少,那么您选择哪一个可能无关紧要。

    链表的开销稍高,因为像您所说的,您正在处理可能不连续的内存块和(有效地)指向下一个元素的指针。这可能不是一个重要的因素,除非你正在处理数百万条条目。

        8
  •  11
  •   Chris May    15 年前

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

    所以,如果你只是在重复列表,而且做了很多,那么 也许吧 值得优化。否则我会使用这个列表,因为当你 需要优化代码。

    N.B.I 注意使用 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();
            }
        }
    }
    
        9
  •  6
  •   CookieOfFortune    15 年前

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

        10
  •  5
  •   tpdi    15 年前

    如果你有几千个,考虑使用trie。trie是一个类似树的结构,它合并了存储字符串的常见前缀。

    例如,如果字符串

    intern
    international
    internationalize
    internet
    internets
    

    Trie将存储:

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

    字符串需要57个字符(包括空终止符,“\0”)用于存储,再加上容纳它们的字符串对象的大小。(事实上,我们应该把所有的大小四舍五入到16的倍数,但是…)大致称之为57+5=62字节。

    trie需要29个(包括空终止符“0”)用于存储,再加上trie节点的sizeof,它是对数组的引用和子trie节点的列表。

    对于这个例子,结果可能是一样的;对于数千人来说,只要有公共前缀,结果可能就更少了。

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

    但如果你当时只使用少数——比如说,在字典中查找东西——trie可以为你节省很多空间。绝对比存储在哈希集中的空间小。

    你说你是“连续地”访问它们——如果这意味着按字母顺序,那么trie显然也会给你免费的字母顺序,如果你先对它进行深度迭代的话。

        11
  •  5
  •   Roman Nikitchenko    11 年前

    更新:

    正如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
    

    对“更快”还有什么评论吗?这是完全可以理解的。问题是什么时候3倍的速度比列表的灵活性更好。但这是另一个问题。 顺便说一句,我也检查了这个人工构造的 ArrayList . 结果几乎一样。

        12
  •  5
  •   boraseoksoon    8 年前

    由于这里已经有很多好的答案,我想给你一些其他的实际观点的信息,也就是 插入和迭代性能比较: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

        13
  •  4
  •   Nuoji    15 年前

    请记住,AARYLIST封装了一个数组,因此与使用原始数组相比,几乎没有什么区别(除了在Java中更容易使用列表)。

    与ArrayList相比,选择数组几乎唯一合理的时间是存储基元(如byte、int等),并且需要使用基元数组获得的特定空间效率。

        14
  •  4
  •   Emre Köse    15 年前

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

    1. 如果字符串的数目几乎为常量,则使用数组(或arraylist)。但是如果数字变化太大,那么你最好使用LinkedList。
    2. 如果需要(或将要)在中间添加或删除元素,则必须使用LinkedList。
        15
  •  3
  •   TofuBeer    15 年前

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

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

        16
  •  3
  •   Warrior    15 年前

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

        17
  •  3
  •   Thomas Mauch    12 年前

    如果你能以一个固定的大小生活,阵列将更快,需要更少的内存。

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

    所以你可能想看看 http://java.dzone.com/articles/gaplist-%E2%80%93-lightning-fast-list 介绍了GAPLIST。这个新的列表实现结合了arraylist和linkedlist的优点,几乎对所有操作都有很好的性能。

        18
  •  2
  •   JRalph    14 年前

    取决于实现。一个基元类型的数组可能比arraylist更小,效率更高。这是因为数组将直接将值存储在连续的内存块中,而最简单的ArrayList实现将存储指向每个值的指针。尤其是在64位平台上,这会产生巨大的差异。

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

        19
  •  2
  •   Shehan Simen    11 年前

    清单是Java 1.5及其后的首选方法,因为它可以使用泛型。数组不能有泛型。数组也有一个预先定义的长度,不能动态增长。初始化较大的数组不是一个好主意。 ArrayList是用泛型声明数组的方法,它可以动态增长。 但是,如果更频繁地使用删除和插入,那么链表就是要使用的最快的数据结构。

        20
  •  2
  •   Nik    10 年前

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

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

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

        21
  •  2
  •   jiaweizhang josefx    9 年前

    ArrayList 将其项存储在 Object[] 数组并使用非类型化 toArray 方法,它比键入的方法快得多(蓝色条)。这是类型安全的,因为非类型化数组包装在泛型类型中 ArrayList<T> 由编译器检查。

    enter image description here

    这个图表显示了Java 7上的n=5的基准。但是,图片不会随着更多的项目或其他虚拟机发生太大的变化。CPU开销可能看起来不太大,但会增加。很可能数组的使用者必须将其转换为集合才能对其执行任何操作,然后将结果转换回数组以将其馈送到另一个接口方法等。 使用简单 数组列表 而不是阵列可以提高性能,而不会增加太多的占地面积。 数组列表 向包装数组添加32字节的常量开销。例如,一个 array 对于10个对象,需要104个字节, 数组列表 136字节。

    此操作在恒定时间内执行,因此它比上述任何操作(黄色条)快得多。这与防御拷贝不同。不可修改的集合将在内部数据更改时更改。如果发生这种情况,客户端可能会遇到 ConcurrentModificationException 在迭代这些项时。接口提供的方法 UnsupportedOperationException 在运行时。但是,至少对于内部使用,此方法可以是防御拷贝“的高性能替代品,这是数组不可能实现的。

        22
  •  2
  •   Xtra Coder    8 年前

    没有一个答案有我感兴趣的信息——对同一个数组进行多次重复扫描。必须为此创建JMH测试。

    结果 (Java1.80yxx32,迭代普通数组至少比ARARYLIST快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
  •  2
  •   Apocalisp    8 年前

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

        24
  •  1
  •   Emmanuel Rodriguez    15 年前

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

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

    关于内存消耗。你好像把一些东西混在一起了。数组只会为您所拥有的数据类型提供一个连续的内存块。不要忘记Java有固定的数据类型:布尔、char、int、长、浮点和对象(包括所有对象,甚至数组都是对象)。这意味着,如果声明一个字符串数组[1000]或MyObject MyObjects[1000],则只能获得1000个足够大的内存框来存储对象的位置(引用或指针)。你得不到1000个足够大的内存箱来容纳对象的大小。别忘了,您的对象首先是用“new”创建的。这是在内存分配完成之后,在数组中存储一个引用(它们的内存地址)。对象不会被复制到数组中,只会被它引用。

        25
  •  1
  •   PhiLho    15 年前

    我认为这对弦乐没有真正意义。字符串数组中连续的是对字符串的引用,字符串本身存储在内存中的随机位置。

    数组和列表可以对基元类型产生影响,而不是对对象。 如果 您预先知道元素的数量,不需要灵活性,一个由数百万个整数或双精度数组成的数组在内存中效率更高,速度略高于列表,因为实际上它们将被连续存储并立即访问。这就是为什么Java仍然使用字符串的字符数组,图像数据的数组,等等。

        26
  •  1
  •   Yakov Fain    15 年前

    阵列更快-所有内存都是预先分配的。

        27
  •  1
  •   Alex Hayward    10 年前

    这里给出的许多微基准发现了一些纳秒数,比如数组/数组列表读取。如果一切都在一级缓存中,这是非常合理的。

    更高级别的缓存或主内存访问的数量级可以是10ns-100ns,而一级缓存的数量级可以是1ns。访问一个arraylist有一个额外的内存间接寻址,在一个真正的应用程序中,您可以根据访问之间的代码所做的,从几乎从不到每次都为此付出任何代价。当然,如果你有很多小的数组列表,这可能会增加你的内存使用,使它更有可能出现缓存未命中。

    原来的海报似乎只用了一张,而且在短时间内访问了大量内容,所以应该不会有太大的困难。但对其他人来说可能是不同的,在解释微基准时你应该小心。

    然而,Java字符串是非常浪费的,特别是如果你存储了很多小的字符串(用内存分析器查看它们,对于几个字符的字符串来说,它似乎是60字节)。字符串数组与string对象有间接关系,另一个从string对象指向包含字符串本身的char[]。如果有什么东西会破坏你的一级缓存,就是这个,加上数千或数万个字符串。所以,如果你是认真的-真正认真的-关于尽可能多的刮掉性能,那么你可以考虑用不同的方式来做。例如,您可以持有两个数组,一个char[]中包含所有字符串,一个接一个,以及一个int[]中包含开始的偏移量。这将是一个皮塔做任何事情,你几乎肯定不需要它。如果你选择了,你就选错了语言。

        28
  •  1
  •   Xult    6 年前

    我来这里是为了更好地了解在数组上使用列表对性能的影响。我必须在这里为我的场景调整代码:数组/列表大约1000个int,主要使用getter,这意味着数组[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);
        }
    
        29
  •  0
  •   Vikram    12 年前

    这取决于您必须如何访问它。

    在存储之后,如果您主要想执行搜索操作,只需很少或没有插入/删除,那么可以使用数组(在数组的O(1)中进行搜索,而添加/删除可能需要重新排序元素)。

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

        30
  •  0
  •   Vipin Jain    8 年前

    arraylist内部使用array对象添加(或存储) 元素。换句话说,arraylist由数组数据支持 -结构。arraylist的数组可调整大小(或动态)。

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

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

    数组列表的大小是如何动态增长的?

    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 .