如何在Java中将一个ArrayList从另一个ArrayList中切割出来?

97

如何在Java中获取ArrayList的数组切片?具体来说,我想要做这样的事情:

ArrayList<Integer> inputA = input.subList(0, input.size()/2);
// where 'input' is a prepouplated ArrayList<Integer>

我本以为这个会起作用,但Java返回了一个List - 所以它不兼容。而且当我尝试进行强制类型转换时,Java不允许我这么做。 我需要一个ArrayList - 我该怎么办?


4
你为什么坚持使用 ArrayList?我认为你可能对接口的工作原理了解不够,因为 ListArrayList 不是“不兼容”的——ArrayList 实现了 List,而 List 可能包含你所需的所有必要方法。 - Bombe
6
我坚持使用ArrayList是因为这是一道有着固定方法原型的面试题。但显然我缺乏理解,因为subList应该返回一个List类型,但我却无法将返回的List强制转换为ArrayList。所以你告诉我吧,伙计。 - B T
7
他可能需要一个 ArrayList 是因为他需要调用一个接受 ArrayList 的方法。可以认为这样的方法设计不太好,应该改为接受 List,但是这种情况不仅出现在面试问题中,还可能出现在其他人编写的代码中,而我们不能随意更改同事和库的代码。 - Gravity
请参见以下链接:https://dev59.com/hXM_5IYBdhLWcg3wq1GF - Vadzim
5个回答

147
在Java中,使用接口类型而不是具体类是API中的良好实践。
您的问题在于,您在许多地方可能使用了ArrayList,而实际上应该使用List。因此,您为自己创建了问题,因为列表是ArrayList这一不必要的限制。
以下是您代码应该如何编写的示例:
List input = new ArrayList(...);

public void doSomething(List input) {
   List inputA = input.subList(0, input.size()/2);
   ...
}

this.doSomething(input);

1 - 根据您的评论,“你”实际上是别人……在面试问题中设置了这个问题。这可能实际上是一个诡计问题,旨在看看您如何处理创建与ArrayList分配兼容的(真正的)ArrayList切片。


你提出的解决方案来解决这个问题是/是这样的:

new ArrayList(input.subList(0, input.size()/2))

这是通过复制由sublist调用返回的子列表(切片)来实现的。 由此得到的 ArrayList 不是常规意义上的切片。 它是一个独立的列表。 更改此列表不会更改原始列表,反之亦然。 此外,如果子列表很大,则复制操作将是昂贵的。


如果您受到 API 的限制而无法更改,以至于您必须inputA 声明为 ArrayList,则您可能可以实现一个自定义的 ArrayList 子类,在其中subList 方法返回一个 ArrayList 子类。但是:

  1. 设计、实现和测试将需要大量工作。
  2. 您现在在代码库中添加了一个重要的新类,可能依赖于文档未记录的方面(因此 "受到更改") ArrayList 类的方面。
  3. 您需要更改代码库中创建 ArrayList 实例的相关位置,以创建子类实例。

"复制数组"的解决方案更加实用……请记住这些不是真正的切片。


7
subList方法实际上并不会创建一个新的列表副本,而是返回原始列表的一个视图。(参考链接:http://docs.oracle.com/javase/6/docs/api/java/util/List.html#subList%28int,%20int%29) - Matt
3
@Matthew,实际上我指的是原帖作者的自我回答,他在其中使用了以下代码:new ArrayList(input.subList(0, input.size()/2)) - Stephen C
2
在Java中,使用接口类型而不是具体类来编写API是一个好的实践。 - Ilonpilaaja

8

如果您知道要从ArrayList中删除的元素的startIndex和endIndex,我已经找到了一种方法。

al为原始的ArrayList,startIndexendIndex分别是要从数组中删除的起始和结束索引:

al.subList(startIndex, endIndex + 1).clear();

6
如果没有现有的方法,那么我想你可以从0到input.size()/2迭代,取每个连续的元素并将其附加到一个新的ArrayList中。
编辑:实际上,我认为您可以使用该列表来实例化一个新的ArrayList,使用其中一个ArrayList构造函数

2
这正是我所做的(在阅读您的编辑之前发布了我的答案)。谢谢:) - B T
1
但是那会复制List并创建一个新的ArrayList。 - Joren
2
@BT - 值得一提的是,在这种情况下,“slice”一词通常不是指此意。 - Stephen C

3

尽管这篇文章很旧,但如果有人正在寻找此内容..

Guava可以将List分成指定大小的子列表。

List<Integer> intList = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8);
    List<List<Integer>> subSets = Lists.partition(intList, 3);

Guava Lists文档的快速链接:https://google.github.io/guava/releases/19.0/api/docs/com/google/common/collect/Lists.html - AryanJ-NYC

-3

这是我的解决方法。我忘记了子列表是对原始列表中元素的直接引用,所以它不能正常工作是有道理的。

ArrayList<Integer> inputA = new ArrayList<Integer>(input.subList(0, input.size()/2));

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