将List中前n个元素放入数组中的最快方法

75

如何以最快的方式获取存储在数组中的列表的前n个元素?

将其视为以下情况:

int n = 10;
ArrayList<String> in = new ArrayList<>();
for(int i = 0; i < (n+10); i++)
  in.add("foobar");

选项1:

String[] out = new String[n];
for(int i = 0; i< n; i++)
    out[i]=in.get(i);

选项2:

String[] out = (String[]) (in.subList(0, n)).toArray();

方案3: 有没有更快的方法?也许可以使用Java8流吗?


2
我认为这取决于平台和JVM,你应该自己尝试进行基准测试。 - Elliott Frisch
为什么要依赖于JVM?我该如何计算复杂度? - Joel
复杂度很简单。它是O(n)。实际性能会有所不同。 - Elliott Frisch
两者基本上做的是相同的事情,如果你查看subListtoArray的源代码。 - njzk2
1
只是说一下:如果 n 不至少是百万级别,那么复杂度与所花费的时间无关。实际上,如果这段代码不至少被使用数千次,那么它的性能与你正在开发的应用程序无关。所以,很可能你应该更多地考虑可读性而不是性能。 - undefined
现在,如果你真的处于性能至关重要的情况下(我指的是数十亿个项目和调用),请使用选项1:代码更简单,因此JIT应该工作得更好。但是,在尝试改进代码之前,始终要对整个代码的性能进行分析:时间通常主要消耗在网络通信、IO或我们没有考虑到的地方。 - undefined
7个回答

148

假设:

list - List<String>

使用Java 8 Streams,

  • 获取列表中前N个元素到一个列表中:

    List<String> firstNElementsList = list.stream().limit(n).collect(Collectors.toList());

  • 将列表中前N个元素放入一个数组中:

    String[] firstNElementsArray = list.stream().limit(n).collect(Collectors.toList()).toArray(new String[n]);


为什么这个比#1更快? - Joel
如果我只有一个元素,而我使用以上的流式处理方式,那么我得到的是一个元素和null。 - jagamot
2
如果N小于数组大小,请使用toArray(String[]::new) - Björn Lindqvist
3
.limit(n).toList() 运行得非常好。 - BadPiggie

12

选项1比选项2更快

因为选项2创建了一个新的List引用,然后从List创建了一个n元素的数组(选项1完美地调整了输出数组的大小)。不过,首先需要修复偏移1个单位的错误。使用<(而不是<=)。例如:

String[] out = new String[n];
for(int i = 0; i < n; i++) {
    out[i] = in.get(i);
}

3
令人惊讶的是,subList 方法不会拷贝列表。它仅提供了对现有列表的视图。 - njzk2
除非该视图是无操作的,否则我认为选项1应该更快。 - Elliott Frisch
创建视图基本上是创建一个带有几个变量的对象。没有循环,没有复制,实际上并不多。 - njzk2
toArray有点复杂。 - Elliott Frisch
1
而且更加安全,具有实际的边界检查(例如如果 n >= in.size(),或者如果有人同时更改了列表)。 - njzk2

3

这主要取决于n的大小。

如果n==0,那么选项#1是最好的选择 :)

如果n非常大,toArray(new String[n])会更快。


范围 n 大约为 1-5,而 in 列表的大小大约是原来的两倍。 - Joel
1
非常确定选择方案#1。 - ZhongYu

1

选项3

迭代器比使用get操作更快,因为get操作如果需要进行遍历,则必须从头开始。在ArrayList中可能不会有任何区别,但其他数据结构可能会看到明显的速度差异。这也适用于不是列表的东西,比如集合。

String[] out = new String[n];
Iterator<String> iterator = in.iterator();
for (int i = 0; i < n && iterator.hasNext(); i++)
    out[i] = iterator.next();

0

在你的列表上使用.take(n)操作符


请详细说明您指的是哪个方法?在List接口中没有叫做这样的方法。 - vanje
1
这可以是一个注释。 - Jimale Abdi
@vanje 看起来这个扩展只在 Kotlin 上可用,抱歉让你感到困惑。不过我希望这个答案能够留在这里,因为作为 Kotlin 用户的我也在寻找这个函数时来到了这里。所以其他 Kotlin 用户可以来到这个主题并得到答案。 - FarVoyager

0
arrayList.stream().limit(n).toArray();

n = maxSize in length

这将帮助您获得所需数组的最大大小。

1
为什么这个选项比其他选项更快? - Joel

-6

Use: Arrays.copyOf(yourArray,n);


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接