1.一段BUG引出迭代器
由于公司的代码包含业务逻辑部分,为了方便大家理解,写了一段简化的代码。这段代码是对列表List中的元素进行遍历,并且将满足一定规则的元素(比如不能被3整除的数字)移除。刚开始这段代码是用for循环进行的,运行后会发现,有些元素明明不是3的倍数,却还是存在于链表之中。

            //准备数据
    LinkedList<Integer> list1 = new LinkedList<Integer>();
    LinkedList<Integer> list2 = new LinkedList<Integer>();
    for(int i=0;i<30;i++)
    {
        list1.add(i);
        list2.add(i);
    }
    //for方法移除
    System.out.println("For Remove");
    for(int i =0;i<list1.size();i++)
    {
        Integer num = list1.get(i);
        if(num%3 != 0)
            list1.remove(i);
    }
    for(Integer num : list1)
        System.out.print(num + " ");
    System.out.println();

程序的运行结果如下:

For Remove
0 2 3 5 6 8 9 11 12 14 15 17 18 20 21 23 24 26 27 29
对此,下面这段代码进行了改进,采用迭代器依此访问各元素。

//迭代器Iterator方法移除

    System.out.println("Iterator Remove");
    Iterator<Integer> iter = list2.iterator();
    while(iter.hasNext())
    {
        Integer num = iter.next();
        if(num%3 != 0) {
            iter.remove();
        }
    }
    for(Integer num : list2)
        System.out.print(num + " ");
    System.out.println();

运行结果如下:

Iterator Remove
0 3 6 9 12 15 18 21 24 27
可以看的这次程序可以正常的运行了,不会漏掉remove()操作后的一些元素。

这个例子给我们的启示有两条:

在for循环里面可以访问元素列表,但最好不要对元素列表进行移除操作;
如果想要在遍历过程中移除不合适的元素,最好使用迭代器;
2.迭代器的相关介绍
迭代器是java定义的一个接口,在java.util.Iterator包下。该接口有四大方法,便于实现了该接口的集合类进行访问操作。

public interface Iterator<E>
{
E next();
boolean hasNextO;
void remove0;
default void forEachRemaining(Consumer<? super E> action);
}
在很多的集合中已经存在了访问的方法,如get(),为什么还需要迭代器Iterator这种接口的存在呢?这是因为,在LinkedList集合中,迭代器的访问效率是要高于get方法的,测试代码如下:

public static void ForMethod(List<Integer> list)

{
    long start = System.currentTimeMillis();
    for(int i=0;i<list.size();i++)
    {
        Integer num = list.get(i);
    }
    long t = System.currentTimeMillis() - start;
    System.out.println("ForMethod:" + t + " ms ");
}


public static void IteratorMethod(List<Integer> list)
{
    long start = System.currentTimeMillis();
    Iterator<Integer> iter = list.iterator();
    while(iter.hasNext())
    {
        Integer num = iter.next();
    }
    long t = System.currentTimeMillis() - start;
    System.out.println("IteratorMethod"+ t + " ms ");
}

这里将产生一个大小为100000的链表,对两种方法进行测试,测试代码如下:

    LinkedList<Integer> list = new LinkedList<Integer>();
    for(int i=0;i<100000;i++)
    {
        list.add(i);
    }
    ForMethod(list);
    IteratorMethod(list);

程序的运行结果发现两者的差距是非常的大的,几乎是近千倍的比率。

ForMethod:4327 ms
IteratorMethod 5 ms
这是因为链表中的get方法每次都是从头部查找,直到找到第i个元素为止。而Iterator方法更像以前学数据结构的时候,对链表进行依此遍历。也就是说,对于大小为n的链表,采用get方法的时间复杂度是 n(n+1)/2,而采用迭代器的时间复杂度是n。随着链表大小的增加,访问效率差距的呈线性增长。

谢谢大家能够耐着性子读到这里,希望对大家有帮助,如果喜欢的话请双击一下屏幕!笔心啦!


Scavenger
12 声望1 粉丝

« 上一篇
安装JDk1.8
下一篇 »
git