zoukankan      html  css  js  c++  java
  • 反向思维的妙处解决正向循环删除元素的问题

         循环是我们代码中最常使用的结果,在遍历的基础上进行其他操作,比如删除。如果是使用List容器,那么就更加简单了,因为List封装了许多实用的方法,拿删除来说,就有remove()和removeAll()。拿来主义固然是好事,但是不注意拿来的东西到底怎么用,就会出问题。鲁迅的文章早已经指出这点,所以,我们也要对我们”拿来“的东西研究一下。

    remove()首当其冲就给了我一个”下马威"。remove()这个方法最大的毛病就是改变List的结构,它会将List中想要移除的元素后面的所有元素向前移动一位。我们可以通过下面的代码来看看这个“可怕”的副作用:

          代码如下:

             

        public static void main(String[] args) {
            List<String> list = new ArrayList<String>();
            list.add("java");
            list.add("C++");
            list.add("java");
            list.add("java");
            list.add("java");
            list.add("C");
            System.out.println("看看原先要删除的元素所在的位置:");
            for (int i = 0, len = list.size(); i < len; i++) {
                if (list.get(i).equals("java")) {
                    System.out.println("location:" + i + ":" + list.get(i));
                }
    
            }
            System.out.println("看看是哪些位置的元素被删除:");
            for (int i = 0, len = list.size(); i < len; i++) {
                if (list.get(i).equals("java")) {
                    System.out.println("delete:" + i + ":" + list.get(i));
                    list.remove(i);
                }
            }
            System.out.println("看看现在那些剩下的元素的位置:");
            for (int i = 0, len = list.size(); i < len; i++) {
                System.out.println(i + ":" + list.get(i));
            }
        }

    输入结果如下:

    看看原先要删除的元素所在的位置:
    location:0:java
    location:2:java
    location:3:java
    location:4:java
    看看是哪些位置的元素被删除:
    delete:0:java
    delete:1:java
    delete:2:java
    看看现在那些剩下的元素的位置:
    0:C++
    1:java
    2:C

           看到没有?有一个元素是没有被删除的,而且删除的位置与它们原来的位置也不一样啊!我们把这个过程模拟出来:java(0),C++(1),java(2),java(3),java(4),C(5) --->C++(0),java(1),java(2),java(3),C(4)--->C++(0),java(1),java(2),C(3)--->C++(0),java(1),C(2)。这里面要删除的是两个java(1)!!但是remove()中的每个索引参数只会被删除一次,所以,倒数第二个java是不会被删除的!!

          如果大家还不信,我还有一个验证代码可以做证据:

         

        public static void main(String[] args) {
            List<RatingBook> list = new ArrayList<RatingBook>();
            RatingBook book1 = new RatingBook();
            book1.setBook("java");
            book1.setRating(0);
            RatingBook book2 = new RatingBook();
            book2.setBook("C++");
            book2.setRating(1);
            RatingBook book3 = new RatingBook();
            book3.setBook("java");
            book3.setRating(2);
            RatingBook book4 = new RatingBook();
            book4.setBook("java");
            book4.setRating(3);
            RatingBook book5 = new RatingBook();
            book5.setBook("java");
            book5.setRating(4);
            RatingBook book6 = new RatingBook();
            book6.setBook("C");
            book6.setRating(5);
            list.add(book1);
            list.add(book2);
            list.add(book3);
            list.add(book4);
            list.add(book5);
            list.add(book6);
            for (int i = 0, len = list.size(); i < len; i++) {
                if (((list.get(i).getBook()).equals("java"))) {
                    list.remove(i);
                }
            }
            for (int i = 0, len = list.size(); i < len; i++) {
                System.out.println(i + ":" + list.get(i).getBook() + " "
                        + list.get(i).getRating());
            }
        }

          RatingBook的代码如:

         

    public class RatingBook {
        private int rating;
        private String book;
    
        public void setRating(int rating) {
            this.rating = rating;
        }
    
        public void setBook(String book) {
            this.book = book;
        }
    
        public String getBook() {
            return book;
        }
    
        public int getRating() {
            return rating;
        }
    
    }

    结果如下:

    0:C++ 1
    1:java 3
    2:C 5

          留下来的是倒数第二个java啊!!!

          所以,在循环中使用remove()来删除重复的元素必须打起十二分精神。这个问题的解决也不难,犯不着从此不用remove(),它依然是非常好用的,只是我们会不会用而已。我这里有一个小技巧,无论序列中是否有重复的元素都可以使用,就是反向遍历List然后删除元素。道理很简单,既然remove()是将目标元素后面的元素向前移动,那么,只要目标元素后面没有元素不就可以了?反向就是利用了这点,只要将我上面的代码改成这样:

        

    for(int i = list.size() - 1; i >= 0; i++){
        ...
    }

          这样子处理List是比较好的,因为它可以避免我们使用remove()这些会改变结构的方法带来的影响,也可以一定程度上提高我们的循环效率。循环是程序中耗费最大的一块,关于它的优化的话题一直都是重点,层出不穷,这里就是一点。我们很少注意到,int i = 0; i < list.size()是要付出很大的开销的,因为我们每次判断循环终点条件的时候都会执行一次list.size()的计算,如果list很大的话,开销就很大了。反向的话,list.size()是放在前面的,就不用每次都计算了。

     如果只是想要完全删除重复的元素,我们可以使用removeAll()。它里面的参数类型是Collection<?>,是一个容器,至于为什么是容器,接下来我会讲的,就是有关于removeAll()的原理。

             先上代码:

            

    public static void main(String[] args) {
            List<String> list = new ArrayList<String>();
            list.add("java");
            list.add("C++");
            list.add("java");
            list.add("java");
            list.add("java");
            list.add("C");
            System.out.println("看看原先要删除的元素所在的位置:");
            for (int i = 0, len = list.size(); i < len; i++) {
                if (list.get(i).equals("java")) {
                    System.out.println("location:" + i + ":" + list.get(i));
                }
    
            }
            List<String> sublist = new ArrayList<String>();
            sublist.add("java");
            list.removeAll(sublist);
            System.out.println("看看现在那些剩下的元素的位置:");
            for (int i = 0, len = list.size(); i < len; i++) {
                System.out.println(i + ":" + list.get(i));
            }
        }

    结果如:

    看看原先要删除的元素所在的位置:
    location:0:java
    location:2:java
    location:3:java
    location:4:java
    看看现在那些剩下的元素的位置:
    0:C++
    1:C

           之所以不像上面那样显示要删除的元素的位置,是因为这个过程是将整个list都遍历一次,然后将与sublist相同的元素找出来并且删除掉,而且因为是容器,你可以添加更多的元素,反正只要list中有sublist的元素,就会全部删掉。这样就会避免上面的错误,但是,必须注意,最好是原先的list是怎样的形式,作为参数的容器也是同样的形式,以免出现不可匹配的问题。

          至于如何在循环中删除重复的元素,欢迎看一下我的文章:http://www.cnblogs.com/wenjiang/archive/2012/10/24/2737746.html

  • 相关阅读:
    删除数据库时报错 ERROR 1010 (HY000): Error dropping database (can't rmdir './cart', errno: 39)
    多线程异步操作导致异步线程获取不到主线程的request信息
    使用mybatis更新数据时 时间字段的值自动更新
    mysql死锁com.mysql.cj.jdbc.exception.MYSQLTransactionRollbackException Deadlock found when trying to get lock;try restarting transaction
    mysql 执行报错:Error querying database. Cause: java.sql.SQLSyntaxErrorException:which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
    Linux系统下部署eleasticsearch+kibana
    navicat突然连接不上远程linux服务器上的mysql
    即使是一条咸鱼,也要做最咸的那条
    linux环境下安装jdk,tomcat
    SpringBoot如何内置Tomcat
  • 原文地址:https://www.cnblogs.com/wenjiang/p/2686555.html
Copyright © 2011-2022 走看看