我必须在内存中保留数千个字符串,以便在Java中串行访问。我应该把它们存储在数组中还是应该使用某种列表?
由于数组将所有数据保存在一个连续的内存块中(与list不同),使用数组存储数千个字符串会导致问题吗?
我必须在内存中保留数千个字符串,以便在Java中串行访问。我应该把它们存储在数组中还是应该使用某种列表?
由于数组将所有数据保存在一个连续的内存块中(与list不同),使用数组存储数千个字符串会导致问题吗?
当前回答
这取决于你如何访问它。
存储后,如果你主要想做搜索操作,很少或不需要插入/删除,那么就去数组(因为在数组中搜索是在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的实现方式。
我来这里是为了更好地感受使用列表而不是数组对性能的影响。我不得不为我的场景调整代码:数组/列表的~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);
}
使用哪一种取决于问题本身。我们得看看大O。
图片来源:https://github.com/egonSchiele/grokking_algorithms
数组-当我们必须实现更快的结果获取时,它总是更好的
列表——执行插入和删除的结果,因为它们可以在O(1)中完成,这也提供了方便地添加、获取和删除数据的方法。更容易使用。
但是始终记住,当数据存储在数组中的索引位置是已知的时,数据的抓取将是快速的。
这可以通过对数组排序很好地实现。因此,这增加了获取数据的时间(即;存储数据+排序数据+寻找数据所在的位置)。因此,这增加了从数组中获取数据的额外延迟,即使它们可能擅长更快地获取数据。
因此,这可以用三元数据结构或三元数据结构来解决。如上所述,树数据结构在搜索数据时非常有效,对特定单词的搜索可以在O(1)量级上完成。当时间紧迫时;如果你必须快速搜索和检索数据,你可以使用三种数据结构。
如果你希望你的内存空间消耗更少,你希望有一个更好的性能,那么使用三元数据结构。这两个都适合存储大量的字符串(例如;比如字典里的单词)。
如果提前知道数据有多大,那么使用数组会更快。
List更加灵活。你可以使用由数组支持的数组列表。