使用for循环从链表中移除元素明显比使用迭代器慢。

huangapple go评论101阅读模式
英文:

Removing elements from LinkedList with a for loop is significantly slowed than removing with iterator

问题

我有一个包含从0到100000的整数元素的链表。我尝试使用迭代器和传统的for循环从列表的远端删除元素。我发现迭代器比for循环快大约500倍。我认为for循环明显较慢的原因是因为remove(item)方法,但不确定。是什么使迭代器的删除更快?

以下是代码:

public static void iteratorRemove() {
    Iterator<Integer> iterator = list.iterator();
    while (iterator.hasNext()) {
        int item = iterator.next();
        if (item == 99999) {
            iterator.remove();
        }
    }
}

public static void linkedListRemove() {
    for(int i=0; i<list.size(); i++) {
        if(list.get(i)==99997) {
            list.remove(i);
        }
    }
}
英文:

I have a linked list of integer that contains elements from 0 to 100000.
I tried removing elements from far end of the list using iterator and traditional for loop. What I found is that the iterator is way faster than a for loop roughly 500 times faster. I assume the reason why for loop is significantly slower is because of the remove(item) method but not sure. What makes the iterator remove quicker?

Here is the code:

public static void iteratorRemove() {

	Iterator&lt;Integer&gt; iterator = list.iterator();
	while (iterator.hasNext()) {
	    int item = iterator.next();
	    if (item == 99999) {
	        iterator.remove();
	    }
	}
}

public static void linkedListRemove() {

	for(int i=0; i&lt;list.size(); i++) {
		
		if(list.get(i)==99997) {
			list.remove(i);
		}
	}

}

答案1

得分: 3

Iterator.remove() 方法比 LinkedList.remove(int index)LinkedList.remove(Object o) 方法更高效,因为它直接操作迭代器当前所在的节点。它不需要从链表的开头开始遍历以查找要移除的项。

当调用 iterator.remove() 时,迭代器已经知道要移除的项的位置,因为它是由 iterator.next() 返回的最后一项。因此,它可以在常数时间 O(1) 内移除该项,即所需的时间不依赖于列表的大小。

另一方面,LinkedList.remove(int index)LinkedList.remove(Object o) 方法必须首先找到要移除的项。在最坏的情况下,它们可能需要遍历整个列表以查找该项。链表不支持对元素的高效随机访问,因此查找项是一个 O(n) 操作,其中 n 是列表的大小。一旦找到该项,可以在常数时间内移除,但耗时的部分是查找该项。

这就是为什么使用 iterator.remove() 的代码比使用 LinkedList.remove() 的代码快得多的原因。前者不需要反复遍历列表以查找要移除的项。

此外,关于使用 LinkedList.remove() 的代码的一个小问题是:每次从列表中移除一个项时,被移除项后面的所有项的索引都会改变。因此,for 循环可能会跳过一些项,或者因为 i 变大而导致 IndexOutOfBoundsException。在迭代期间从列表中移除项的常见模式是从后向前迭代。然而,在链表的情况下,由于它不支持对元素的高效随机访问,最好使用迭代器,以避免每次移除时都需要遍历列表。

public static void linkedListRemove() {
    ListIterator<Integer> listIterator = list.listIterator(list.size());
    while (listIterator.hasPrevious()) {
        int item = listIterator.previous();
        if(item == 99997) {
            listIterator.remove();
            // 如果找到并移除了该项,就跳出循环
            break;
        }
    }
}
英文:

The Iterator.remove() method is more efficient than LinkedList.remove(int index) or LinkedList.remove(Object o) methods because it operates directly on the node that the iterator is currently on. It doesn't need to traverse the LinkedList from the beginning to find the item to remove.

When you call iterator.remove(), the iterator already knows the location of the item to remove, because it's the last item that was returned by iterator.next(). Therefore, it can remove the item in constant time O(1), i.e., the time it takes doesn't depend on the size of the list.

On the other hand, LinkedList.remove(int index) or LinkedList.remove(Object o) methods have to first find the item to remove. In the worst case, they might need to traverse the entire list to find the item. LinkedLists don't support efficient random access to elements, so finding an item is an O(n) operation, where n is the size of the list. Once the item has been found, it can be removed in constant time, but the time-consuming part is finding the item.

This is why your code using iterator.remove() is much faster than the one using LinkedList.remove(). The former doesn't need to repeatedly traverse the list to find the items to remove.

Also, a minor point about your code using LinkedList.remove(): every time you remove an item from the list, the indexes of all the items that come after the removed item change. Therefore, the for loop might skip some items or run into a IndexOutOfBoundsException because i becomes greater than the current size of the list. A common pattern when removing items from a list during iteration is to iterate from the end to the beginning. However, in the case of LinkedList, since it doesn't support efficient random access to elements, it's best to use an Iterator to avoid having to traverse the list for each removal.

public static void linkedListRemove() {
    ListIterator&lt;Integer&gt; listIterator = list.listIterator(list.size());
    while (listIterator.hasPrevious()) {
        int item = listIterator.previous();
        if(item == 99997) {
            listIterator.remove();
            // break the loop if we have found and removed the item
            break;
        }
    }
}

答案2

得分: 2

list.get(i) 必须在 LinkedList 中从 0 步行到 i,这使得该方法的时间复杂度为 O(N^2)。这就是 LinkedListArrayList(随机访问)的区别。使用 Iterator 时,您只是访问每个元素一次。

英文:

list.get(i) has to walk from 0 to i in a LinkedList, which makes that approach take O(N^2). That is the distinction of LinkedList vs ArrayList (random access). With an Iterator you are just accessing each element once.

huangapple
  • 本文由 发表于 2023年8月4日 21:37:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/76836458.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定