在 Java 中从 ArrayList 中删除对象
Posted
技术标签:
【中文标题】在 Java 中从 ArrayList 中删除对象【英文标题】:Deleting objects from an ArrayList in Java 【发布时间】:2010-11-21 14:02:01 【问题描述】:如果满足条件,我需要从ArrayList
中删除一些对象,我想知道哪种方式更有效。
情况如下:我有一个包含 ArrayList
的类,其中包含一些其他对象。我必须遍历这个ArrayList
并删除所有满足特定条件的元素。
据我所知,这些将是我删除的选项:
新建ArrayList
,添加不满足条件的元素。迭代后,从旧的数组列表交换到没有元素的新数组。
新建ArrayList
,添加满足条件的元素。迭代后,使用removeAll()
方法将ArrayList
与要删除的对象一起传递。
有没有更有效的方法从ArrayList
中删除对象?
【问题讨论】:
除非你真的确定在代码中的这个特定点性能是一个问题,否则我建议忽略效率。您还应该考虑其他一些事情,例如:您是否在其他应反映更改的地方保留对原始列表的引用?那么你不能使用1。你可以使用ArrayList.remove()
,i。 e. equals()
的语义是否像列表中的对象所需的那样工作?
嗯,我正在谈论的对象包含一些数组列表,我必须对它们都做同样的事情。我不知道这是否会成为瓶颈(我还没有测试过),但我想知道你们如何删除项目,看看我是否有更好的选择。回答你的第二个问题:是的,我可以使用 remove() 方法。
【参考方案1】:
另一种方式:迭代器有一个可选的 remove() 方法,它是为 ArrayList 实现的。您可以在迭代时使用它。
不过我不知道,哪个变体的性能最好,你应该测量一下。
starblue 评论说,复杂性不好,这是真的(对于 removeAll() 也是如此),因为 ArrayList 必须复制所有元素,如果中间是添加或删除的元素。对于这种情况,LinkedList 应该更好地工作。但是,由于我们都不了解您的真实用例,因此最好测量所有变体,以选择最佳解决方案。
【讨论】:
remove()
有一个警告:它可能不会删除您当前正在查看的对象。引用JavaDoc:更正式地说,删除具有最低索引的元素 i 使得 (o==null ? get(i)==null : o.equals(get(i))) (如果存在这样的元素)。 " 因此,根据该列表中的对象,它可能无法按预期工作。基本上,如果您可以将 ArrayList 交换为 SortedSet 而不会损害您的应用程序,那么您应该没问题。
这是我在 javadoc 中看到的 Iterator 的 remove() 方法:“从底层集合中删除迭代器返回的最后一个元素(可选操作)。此方法每次只能调用一次"我错过了什么?
感谢您的所有回答,但我认为 Mnementh 应该得到正确的答案,因为他是第一个回答的人。
这比问题中的两个提案更糟糕,因为它会导致 O(n²) 运行时间。
这也很糟糕,因为如果在遍历列表时抛出异常,那么列表将处于不一致的状态。如果列表不是方法本地的,这可能是一个问题。【参考方案2】:
也许Iterator 的remove() 方法? JDK 的默认集合类应该都是支持该方法的创建者迭代器。
【讨论】:
【参考方案3】:您可以在遍历 ArrayList 时向后迭代并删除。这样做的好处是后续元素不需要移动,并且比向前移动更容易编程。
【讨论】:
不错,+1 :) 想知道这种性能改进是否真的很明显。 在某些情况下是的——如果你担心的话,可能是其中之一。通常我采取的策略是建立另一个要删除的项目列表——其他程序员更容易理解。 谢谢,Richard,这是我从未想过的有趣方法! +1 是的,满足要求的好方法,甚至在其他语言中也是一种通用方法。 +1。 从列表的头部或尾部遍历是否找到该元素并不重要 - 该元素的实际删除肯定会涉及移动其余成员(它们是被删除元素的右侧)。就实际删除而言,您的解决方案对我没有意义。如果您知道该元素位于列表末尾的某个位置,则向后迭代只会有利于搜索该元素。否则,您的解决方案与向前迭代没有什么不同,因为删除时会发生相同数量的移位。【参考方案4】:首先,我会确保这确实是一个性能瓶颈,否则我会选择最简洁、最具表现力的解决方案。
如果这是性能瓶颈,只需尝试不同的策略,看看哪种策略最快。我的赌注是创建一个新的 ArrayList 并将所需的对象放入其中,丢弃旧的 ArrayList。
【讨论】:
【参考方案5】:显然,在您提到的两种方法中,第 1 种方法更有效,因为它只需要遍历列表一次,而使用方法 2,列表必须遍历两次(首先找到要删除的元素,并删除它们)。
实际上,从另一个列表中删除元素列表可能是一种比 O(n) 更糟糕的算法,因此方法 2 更糟糕。
迭代器方法:
List data = ...;
for (Iterator i = data.iterator(); i.hasNext(); )
Object element = i.next();
if (!(...))
i.remove();
【讨论】:
就像你使用迭代器进入for的方式一样!【参考方案6】:我猜,大多数性能会使用listIterator
方法并进行反向迭代:
for (ListIterator<E> iter = list.listIterator(list.size()); iter.hasPrevious();)
if (weWantToDelete(iter.previous())) iter.remove();
编辑: 很久以后,人们可能还想使用 lambda 或方法引用从列表(或任何集合!)中添加 the Java 8 way of removing elements。如果您愿意,可以使用就地 filter
进行收藏:
list.removeIf(e -> e.isBad() && e.shouldGoAway());
这可能是清理集合的最佳方式。由于它使用内部迭代,集合实现可以采取捷径使其尽可能快(对于ArrayList
s,它可以最大限度地减少所需的复制量)。
【讨论】:
【参考方案7】:除非你确定你面临的问题确实是一个瓶颈,否则我会选择可读的
public ArrayList filterThings()
ArrayList pileOfThings;
ArrayList filteredPileOfThings = new ArrayList();
for (Thing thingy : pileOfThings)
if (thingy.property != 1)
filteredPileOfThings.add(thingy);
return filteredPileOfThings;
【讨论】:
【参考方案8】:我很喜欢 Mnementh 的建议。 不过,只有一个警告,
ConcurrentModificationException
请注意,您运行的线程不超过一个。如果执行多个线程并且线程没有很好地同步,则可能会出现此异常。
【讨论】:
不完全正确 - 如果底层集合在迭代时发生变化,这将从快速失败集合中抛出。例如,您对 lst 进行迭代,其中一个调用是直接在 lst 上调用 remove,而不是使用 ListIterator 上的 remove 方法。【参考方案9】:从 ArrayList 中删除元素有一个隐藏的成本。每次删除元素时,都需要移动元素以填充“洞”。平均而言,这将为具有 N 个元素的列表分配 N / 2
分配。
因此,从 N 个元素的 ArrayList 中删除 M 个元素平均为 O(M * N)
。 O(N) 解决方案涉及创建一个新列表。例如。
List data = ...;
List newData = new ArrayList(data.size());
for (Iterator i = data.iterator(); i.hasNext(); )
Object element = i.next();
if ((...))
newData.add(element);
如果 N 很大,我的猜测是对于小到 3 或 4 的 M 值,这种方法将比 remove
方法更快。
但重要的是创建足够大的newList
以容纳list
中的所有元素,以避免在扩展后备数组时复制它。
【讨论】:
【参考方案10】:我找到了另一种更快的解决方案:
int j = 0;
for (Iterator i = list.listIterator(); i.hasNext(); )
j++;
if (campo.getNome().equals(key))
i.remove();
i = list.listIterator(j);
【讨论】:
【参考方案11】:int sizepuede= listaoptionVO.size();
for (int i = 0; i < sizepuede; i++)
if(listaoptionVO.get(i).getDescripcionRuc()==null)
listaoptionVO.remove(listaoptionVO.get(i));
i--;
sizepuede--;
编辑:添加缩进
【讨论】:
【参考方案12】:使用迭代器,您可以始终处理要排序的元素,而不是指定的索引。所以,你不应该为上面的事情而烦恼。
Iterator itr = list.iterator();
String strElement = "";
while(itr.hasNext())
strElement = (String)itr.next();
if(strElement.equals("2"))
itr.remove();
【讨论】:
【参考方案13】:虽然这是反直觉的,但这是我大大加快此操作的方式。
正是我在做什么:
ArrayList < HashMap < String , String >> results; // This has been filled with a whole bunch of results
ArrayList
results.removeall(丢弃);
但是,remove all 方法需要 6 秒以上(不包括获取丢弃结果的方法)才能从 2000 个数组中删除大约 800 个结果(ish)。
我尝试了 gustafc 和其他人在这篇文章中建议的迭代器方法。
这确实稍微加快了操作速度(降低到大约 4 秒),但这仍然不够好。所以我尝试了一些冒险的事情......
ArrayList < HashMap < String, String>> results;
List < Integer > noIndex = getTheDiscardedIndexs(results);
for (int j = noIndex.size()-1; j >= 0; j-- )
results.remove(noIndex.get(j).intValue());
虽然 getTheDiscardedIndexs 保存的是索引数组而不是 HashMap 数组。事实证明,这加快了删除对象的速度(现在大约 0.1 秒)并且内存效率更高,因为我们不需要创建大量结果来删除。
希望这对某人有所帮助。
【讨论】:
以上是关于在 Java 中从 ArrayList 中删除对象的主要内容,如果未能解决你的问题,请参考以下文章
[初学者Java删除ArrayList Java问题中的对象