`

技巧:ArrayList删除元素时, 从尾部开始遍历,可大大提升执行效率

 
阅读更多

一.描述:

    1. 工作中,常常遇到这样的要求: 将列表里符合(或不符合)某条件的元素删除, 如:

        有列表list = [ "a", "b", "c", "d" ], 删除其中的"a", "b", "c"

    2. 关键在于遍历: 建议从尾部开始, 取代常规的从头部开始

    3. 有人会说 使用 LinkedList 更合适 -- 此处只考虑 ArrayList;

 

二.推断:

    当 list 删除元素 "a" 之后, 变为 [ "b", "c", "d" ], 猜想, list 内部数组发生变化(内存的变化):

        list(1) --> list(0),

        list(2) --> list(1),

        list(3) --> list(2),

        list(4)删除

   

    list(n):表示list内部数组的元素;

    -->    :表示复制到,或移动到

 

三.证明:

            查看源码java.util.ArrayList.remove()

   

public E remove(int index) {
        RangeCheck(index);

        modCount++;
        E oldValue = (E) elementData[index];

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index + 1, elementData, index, numMoved);
        elementData[--size] = null; // Let gc do its work

        return oldValue;
    }

            里面的System.arraycopy(),正是将 欲删除的元素 后面的 元素, 依次向前移动一个位置(内存), 最后再将

   最后一个元素删除(值空, 删除由GC处理).

            与预想的吻合.

 

四.分析:

    1.从list头部开始遍历:

       列表元素                            删除元素        操作后列表元素           内存移动次数

     ----------------------------------------------------------------------------------------------------

       [ "a", "b", "c", "d" ]             "a"            [ "b", "c", "d" ]         3

              [ "b", "c", "d" ]             "b"            [ "c", "d" ]                2

                     [ "c", "d" ]             "c"             [  "d" ]                     1

     ----------------------------------------------------------------------------------------------------

                               合计                                                              6

 

    2.从list尾部开始遍历:

       列表元素                            删除元素        操作后列表元素           内存移动次数

     ----------------------------------------------------------------------------------------------------

       [ "a", "b", "c", "d" ]             "c"            [ "a", "b", "d" ]         1

             [ "a", "b", "d" ]             "b"            [ "a", "d" ]                1

                    [ "a", "d" ]             "a"            [  "d" ]                      1

     ----------------------------------------------------------------------------------------------------

                               合计                                                              3

 

    3.综上两点, 从list尾部开始遍历 可以减少底层的操作次数, 提高程序执行得效率.

 

五.实践:

   

    此例, 删除了99999个元素(共100000), 免得有人投机取巧用clear,当然用clear是最快的,因为不涉及内存移动. 

 

import java.util.ArrayList;
import java.util.List;

public class $ {

    private static int MAX = 100000;

    public static void main(String[] args) {
        System.out.println("容量:" + MAX);
        removeFromF();
        removeFromL();
    }

    private static void removeFromF() {

        List data = initData();

        long l0 = System.currentTimeMillis();
        for (int i = 0; i < data.size() - 2; i++) {
            data.remove(i);
        }
        long l1 = System.currentTimeMillis();

        System.out.println("从前往后remove(留下最后一个):" + (l1 - l0) + "MS");
    }

    private static void removeFromL() {

        List data = initData();

        long l0 = System.currentTimeMillis();
        for (int i = data.size() - 2; i >= 0; i--) {
            data.remove(i);
        }
        long l1 = System.currentTimeMillis();

        System.out.println("从后往前remove(留下最后一个):" + (l1 - l0) + "MS");
    }

    private static List initData() {
        List data = new ArrayList();
        for (int i = 0; i < MAX; i++) {
            data.add(i);
        }
        return data;
    }
}

 

    结果:

   

容量:100000
从前往后remove(留下最后一个):3596MS
从后往前remove(留下最后一个):6MS

 

   这耗时, 不是一个数量级的,随着数据增大, 差距越明显.

 

六.番外:

 

    随便记一下:

   

    public E remove(int index) {
        RangeCheck(index);

        modCount++;
        E oldValue = (E) elementData[index];

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index + 1, elementData, index, numMoved);
        elementData[--size] = null; // Let gc do its work

        return oldValue;
    }

 

   当做其删除操作时, list 内部数组的大小是没有变化的(假如此时GC尚未工作), 变化的只是size, 而在外部我们能得到的

是size, 最多也只能得到 0 ~ size-1 的元素.

 

七:扩展:

 

    手动删除excel列时,也建议 从后面开始删. 源码没有看过,是根据现象猜测的.

 

    举例:

    excel2007 (2003没试过,猜想应该是一样的)

    从A ~ BB列, 20000行数据, 删除其中的A列 和 BB列, 看看哪个快?

    有兴趣可试试....

   

分享到:
评论
1 楼 雨打蕉叶 2013-09-30  
有意思 

相关推荐

    Java中ArrayList和LinkedList区别

    - LinkedList的get操作相对较慢,因为需要从头或尾部开始遍历直到目标位置,时间复杂度为O(n)。 3. 插入和删除性能: - 在ArrayList中插入或删除元素时,可能需要移动后续元素来保持数组的连续性。如果插入或删除...

    java代码-使用ArrayList对员工信息进行添加和显示

    5. **效率考虑**: ArrayList内部基于动态数组实现,因此在尾部添加元素时效率较高。但如果在中间或开头插入或删除元素,可能需要移动大量元素,效率较低。如果插入和删除操作频繁,考虑使用LinkedList。 6. **线程...

    java批量删除列表内容

    5. **异常处理**:在执行批量删除时,应捕获并处理可能出现的异常,例如`NoSuchElementException`或`ConcurrentModificationException`。 6. **性能考量**:批量删除操作的时间复杂度与所选数据结构和删除策略有关...

    Java 30道面试题和答案.docx

    - LinkedList基于双向链表,插入和删除操作在任何位置都很高效,但访问元素时需要从头或尾部开始遍历,效率低。 4. **Array vs ArrayList**:Array是原生的Java数组,而ArrayList是集合框架中的类,基于数组实现,...

    Data Structures Succinctly Part 1

    - **删除方法(Deletion)**:从数组中移除元素。 - `RemoveAt`:根据索引移除元素。 - `Remove`:移除特定值的所有匹配项。 - **索引操作(Indexing)**: - `IndexOf`:查找特定元素的索引。 - `Item`:通过...

    Java数据结构类面试题.docx

    动态数组(如ArrayList)支持随机访问,但插入和删除在非尾部位置较慢,需移动元素。对于1k数据的随机存取,若访问频繁,动态数组更合适;若插入和删除频繁,链表可能更适合。 9. 字节序:字节序是指多字节数据类型...

    数据结构课程设计报告

    ArrayList基于动态数组实现,提供了快速的随机访问,但插入和删除元素时效率相对较低,因为需要移动大量元素。相比之下,LinkedList采用链表结构,插入和删除操作速度快,但随机访问性能较差,因为需要从头或尾部...

    java代码-使用集合ArrayList对字符串进行存储和管理。

    - **插入与删除效率**:在尾部插入和删除元素速度较快,但在中间或头部操作则较慢,因为需要移动后续元素。 2. **创建ArrayList** ```java ArrayList&lt;String&gt; list = new ArrayList(); ``` 或者指定初始容量:...

    Linkedlist(1)_java_

    LinkedList不同于ArrayList,它的内部是以链表的形式存储元素,因此在插入和删除操作上具有较高的效率,但在随机访问元素时性能相对较差。LinkedList包含节点(Node)对象,每个节点都包含一个数据元素和对下一个...

    基于JAVA实现的常用数据结构代码,JAVA实现复杂度、动态数组、链表、栈、队列、二叉搜索树等

    Java中的LinkedList实现了List接口,提供了高效的插入和删除操作,但在随机访问元素时效率较低,因为需要从头开始遍历。链表分为单向链表和双向链表,其中双向链表可以向前和向后遍历。 4. **栈**:栈是一种后进先...

    java在线列表

    对于频繁的插入和删除操作,LinkedList表现更优,但在随机访问时性能较差,因为需要从头或尾部开始遍历。 3. **Vector**: Vector与ArrayList类似,也是基于数组实现的,但它线程安全。在多线程环境下,Vector是一个...

    java代码-使用集合ArrayList对字符串进行存储和管理

    ArrayList是基于数组实现的,因此可以快速访问任何位置的元素,其索引是从0开始的。它的主要优点是插入和删除元素时,如果位置靠近数组尾部,性能较好。然而,如果在数组中间进行插入或删除操作,由于需要移动后续...

    Java集合知识测试B.pdf

    7. **Vector类**:Vector是从JDK 1.0开始引入的,它是一个线程安全的动态数组,但性能较差,因为它的操作是同步的。ArrayList后来取代了Vector,但在某些旧项目中仍可能被使用。所以选项a和d是正确的,b和c是错误的...

    java知识点复习

    然而,随机访问元素时效率较低,因为需要从头或尾部逐个遍历。 #### Collection与Collections - **概念区分**:`Collection`是所有集合类的根接口,定义了集合的基本行为。`Collections`则是一个工具类,提供了一...

    查询速度调研 1

    1. ArrayList:基于动态数组,随机访问速度快,但在插入或删除元素时,尤其是中间位置,需要移动后续元素,效率较低。 2. LinkedList:适用于频繁插入和删除操作,因为它只需改变相邻节点的引用,但随机访问(查询)...

    Java 面试题

    LinkedList基于双向链表,插入和删除操作高效,但访问元素需要从头或尾部开始遍历,速度较慢。 008异常处理机制,try-catch-finally的作用? 异常处理用于捕获和处理程序运行时可能出现的错误。try块中放置可能抛出...

    Java面试题总结

    每个元素包含对前一个和下一个元素的引用,因此在头部和尾部添加或删除元素非常快。 #### 3. HashMap与HashTable区别 - **数据结构基础**:`HashMap` 实现了`Map`接口,而`HashTable`继承自`Dictionary`类。 - **...

    美团外卖配送部后台开发面经1

    在Java编程中,`synchronized`关键字是用于实现线程同步的关键工具,它确保了多...如果一直在列表尾部添加元素,`ArrayList`的效率更高,因为它只需要简单地调整数组大小即可,而`LinkedList`需要遍历链表来找到尾部。

    Java_Oder_List

    LinkedList则通过双向链表实现,插入和删除操作非常快,但访问元素时需要从头或尾部开始遍历,效率较低。 1. **ArrayList**: - 实现:基于数组,可以动态调整大小。 - 特点:随机访问速度快,增删元素慢(需要...

    企业java面试题(笔试+面试)

    5. **ArrayList, Vector, LinkedList的特性**:ArrayList和Vector都是基于动态数组实现的列表,支持随机访问,但插入和删除元素时需要移动大量元素,效率较低。Vector是线程安全的,性能相对较差。LinkedList使用...

Global site tag (gtag.js) - Google Analytics