zoukankan      html  css  js  c++  java
  • 遍历List删除重复元素的方案探究

          遍历List,然后删除其中的元素,这种操作存在风险,如果改变List的结构,就会影响到我们接下来的操作,这是危险的,甚至有些数据我们在毫不知情的情况下就被删除掉。这都是不允许的,为应对这种情况,我们可以做一个映射,将原来List映射到新的List上,比如说,可以将不需要删除的元素放到一个新的List上,而不是在原有List上直接删除得到,这样更加安全,但是很多情况下,我们根本就不知道数据是怎样的,它哪些是重复的,需要进行判断,但是在循环中如果这样写:

        

          List<Integer>  mlist = new ArrayList<Integer>();
          for(int i = 0; i < list.size() - 1; i++){
              for(int j = i + 1; j < list.size(); j++){
                   if(list.get(i) != list.get(j)){
                      mlist.add(list.get(i));
                    }
                }
            }

          这可不是什么正确的代码,因为你能找出来的,只有根本就不重复的元素,但是重复的元素也是要添加的啊(只是只添加一个而已)。那么,只要找到重复的,然后删除掉就行了,是的,这就是万恶的remove()之所以会被使用的原因。“只要删除就行”,这句话是具有潜在风险的,如果你无法保证删除后的List是安全的,请千万不要这么做。

           List中有两个方法是关于删除元素的,就是remove()和removeAll() 。remove()会改变List的结构,就是它会删除原来List中元素的索引,然后剩下的每个元素都向前移动一位,这样,当我们要对List的下个元素进行操作的时候,该元素的索引已经发生改变,就可能会出现错误。使用removeAll()存在限制,因为它的参数是一个容器(通常都是与操作容器一样类型),它的原理是把这个参数容器里的元素跟List中的元素进行一一比较(使用equals()),但是,如果这是一个对象容器,存储的是对象,如何比较这是一个大问题。基本上,它是无法处理存储对象的容器的,而且它是全部删除,我们这里是删除重复的,removeAll()显然无法符合我们的需要。那么,我们应该怎么做呢?(想要知道更多有关于remove()和removeAll()的东西,可以参考一下我这篇文章:http://www.cnblogs.com/wenjiang/archive/2012/09/15/2686555.html
         使用迭代器的话,就使得这变成可能。迭代器的原理,是保留原有List元素的索引,这样就不会破坏List的结构。我们来看一下代码实现:
        
           Itreator iterator = list.iterator();
           while(iterator.hasNext()){
                    Integer num = iterator.next();
                    if(num == 2){
                       iterator.remove();
                     }
             }

           这样确实能够安全的删除我们想要删除的元素,但是,它依然不能解决我们上面的问题,因为我们根本就不知道重复的元素到底是什么。 但是这里还是要说一下,迭代器的处理功能是很强大的,它能够处理对象容器,可以完全匹配也可以不用完全匹配,就像下面这样:

         

                    List<Book> list = new ArrayList<Book>();   //Book对象包含两个数据,名字name和书号number
                   //假设list里面两本书,(java, 1)和(java, 2)
                    Itreator iterator = list.iterator();
                    while(iterator.hasNext()){
                           Book book = (Book)iterator.next();
                           if((book.getName()).equals("java")){  //删除所有名字有java的书
                                iterator.remove();
                            }
                           if((book.getName()).equals("java")) && ((book.getNumber()) == 2)){  //删除(java, 2)
                                iterator.remove();
                            }
                     }

          要想真正解决我们上面的问题,需要使用到Map中key-value的唯一性问题。我们知道,一个key只能存储一个value,而且key是唯一的,如果我们的数据中,一个key存在多个value,那么,取最后一个value。根据这个特性,我们就能做点事情了。

           我们来举个例子,就像这样的数据:2,2,2,2,3,3,4,6,5,2,4,7,6,5,我们想要一个不重复的数列,利用map,我们可以这样做:
          
    int[] input = new int[]{ 2,2,2,2,3,3,4,6,5,2,4,7,6,5 };
    Map<Integer, Integer> output = new HashMap<Integer, Integer>();
    for(int i = 0; i < input.length; i++) {
        output.put(input[i], 0);
    }
    for(int i : output.keySet()) {
       System.out.println(i);
    }

         因为每个key只能存储一个value,而且key是唯一的,将我们的目标数据变成key,然后取出来,我们就能解决这个问题。

         但是我们要解决的是对象容器的问题,那么,map是否也可以呢?答案是可以的。
         还是Book的例子。我们的book可能存在这样的重复:(java, 0), (java, 0),(c,1),(c++,2),
         那么,我们就可以这样做:
        
    Map<String, Integer> map = new HashMap<String, Integer>();
    List<Book> list = new ArrayList<Book>();
    for(int i = 0; i < list.size(); i++){
       map.put(list.get(i).getName(), list.get(i).getNumber());
    }
    Set entries = map.entrySet();
    if (entries != null) {
        Iterator iterator = entries.iterator();
        while (iterator.hasNext()) {
        Book book = new Book();
        Map.Entry entry = (Entry) iterator.next();
        String key =(String) entry.getKey();
        Integer value = (Integer)entry.getValue();
        book.setName(key);
        book.setNumber(value);
        }
    }

    这种做法有个局限,就是无法将自定义对象作为Key值,因为Map无法判断两个自定义对象是否相等,因为它内部使用的是equals或==,如果是(java,0),(java,1),(java,0),(c,0),(c,1)这种情况,就很难说哪个能作为key值呢。所以,我们的方案还得继续改进以满足我们一开始的要求。

             上面的例子症结就是Map的equals()无法判断两个自定义对象是否相等,于是,我们的第一个想法就是为什么不自定义自己的equals()方法呢?因为编译器是这样来判断两个对象是否相等的:先是看这个对象是否有自定义的equals()方法,如果没有,再调用默认的equals(),这个equals()是Object类的,它只是比较两个引用的地址。这种比较方法一般情况下是够用的,但是在比较一些更加复杂的对象时就会有问题。但是为什么equals()能够比较String呢?语法上,即使内容相同的两个String对象,也是两个不同的引用,但是在比较的时候还是一样的,因为它们的hashCode()是一样的。所以,如果覆写了Object的equals(),也要顺便覆写hashCode(),因为在比较对象的时候,先调用hashCode() ,如果相同,再调用equals()。

           我们这次选择的容器是Set。我们知道,Set里的元素都是不重复的,但这只对于String和基本类型而言,自定义对象要覆写equals()和hashCode()才能发挥作用。

           覆写hashCode()有很多方法,只要确保散列码不一样就行,一般我们散列码的结果是与我们对象的数据有关,这次我们的对象中包含两个数据:name和rating,所以,hashCode()这么写:

    public int hashCode() {
          return this.name.hashCode() + this.rating;
    }

           接着就是equals():

    public boolean equals(Object obj) {
        RatingBook book = new RatingBook();
        if (obj instanceof RatingBook) {
            book = (RatingBook) obj;
        }
        return ((book.getRating() == this.rating) &(book.getName().equals(this.name)));
    }

           千万不要想当然的的将参数设为RatingBook,因为equals()是不支持泛型的(虽然java后来支持泛型)。这里我先判断obj是否是RatingBook,这样也能顺便判断是否为null。
           然后就是我们的测试类:

    Set<RatingBook> set = new HashSet<RatingBook>();
           set.add(new RatingBook("java", 1));
           set.add(new RatingBook("java", 1));
           set.add(new RatingBook("c", 0));
           set.add(new RatingBook("c", 0));
           set.add(new RatingBook("c", 1));
           set.add(new RatingBook("java", 0));
           set.add(new RatingBook("java", 0));
           set.add(new RatingBook("java", 0));
           Iterator<RatingBook> iterator = set.iterator();
           while (iterator.hasNext()) {
                 RatingBook book = iterator.next();
                 System.out.println(book);
            }

           结果如下:

    c评分:1
    c评分:0
    java评分:0
    java评分:1

           HashMap也是同样的道理,这里就不改了。
           至此,关于这个话题,我们算是讨论完毕了,因为我们想要的效果已经达成了,但是依然是有很多问题亟待我们去研究,因为代码是永远不会有完美的一天。

  • 相关阅读:
    webpack入门
    react中的this.setState()
    Echarts学习之路3(在react中使用)
    Echarts学习之路2(基本配置项)
    react+mobx脚手架搭建多页面开发
    解决使用插件带来的页面弹框滚动穿透问题
    屏蔽微信内置底部前进后退按钮(很迫切的需求)
    input框输入金额处理的解决办法
    git仓库的创建以及本地代码上传
    又发现了一个git clone代码失败时的解决办法
  • 原文地址:https://www.cnblogs.com/wenjiang/p/2737746.html
Copyright © 2011-2022 走看看