【问题标题】:Prevent duplicate entries in arraylist防止arraylist中的重复条目
【发布时间】:2012-04-15 06:23:46
【问题描述】:

假设我创建了一些像这样的对象类

public class thing {
        private String name; 
        private Integer num;

        public oDetails (String a, Integer b) {
            name = a;
            num = b;
        }
...gets/ sets/ etc

现在我想创建一个数组列表来保存一些这样的对象类。

ArrayList<thing> myList = new ArrayList<thing>;
thing first = new thing("Star Wars", 3);
thing second = new thing("Star Wars", 1);
myList.add(first);
myList.add(second);

我想包含某种逻辑,以便在这种情况下......当我们尝试添加对象“second”而不是向 arrayList 添加新对象时,我们将 second.getNum() 添加到 first.getNum ()。因此,如果您要遍历 ArrayList 它将是

"Star Wars", 4

我很难想出一种优雅的方式来处理这个问题。并且随着数组列表的增长,搜索它以确定是否有重复的名称项变得很麻烦。任何人都可以提供一些指导吗?

【问题讨论】:

标签: java arraylist no-duplicates


【解决方案1】:

您必须创建自己的方法来检查 Thing 类的 name 字段是否设置为“星球大战”,然后添加到 Thing 类的相应 num 字段,这是一种可能的解决方案.

另一种解决方案是使用Map,其中name 字段作为键,num 字段作为值。

例如:

public class Thing
{
   private String name;
   private int    num;

   public Thing(String name, int num)
   {
       this.name = name;
       this.num  = num;
   } 
}

public class ThingMap
{
    Map<String, Integer> thingMap; 

    public ThingMap()
    {
       this.thingMap = new HashMap<>();
    }

    public void put(Thing t)
    {
       String  k = t.getName();
       Integer v = t.getNum();

       if(thingMap.get(k) == null) //no entry exists
       {
          thingMap.put(k, v);
       }
       else //entry exists
       {
          //add to the current value
          thingMap.put(k, thingMap.get(k) + v);
       }
    }

    public Integer get(String k)
    {
       return this.thingMap.get(k);
    }
}

public class TestThing
{
   public static void main(String[] args)
   {
      ThingMap tMap = new ThingMap();
      Thing a = new Thing("Star Wars", 3);
      Thing b = new Thing("Star Wars", 1);

      tMap.put(a);
      tMap.put(b);

      System.out.println("Current value: " + tMap.get(a.getName());
   }

}

希望这会有所帮助。

【讨论】:

  • 把地图放到Thing类里是没有意义的
  • @LuiggiMendoza 这只是一个例子。操作员可以将名称放在他们想要的任何位置。
  • 如果Thing是一个模型类,为什么要放一个只存储实际对象数据的容器?
  • @LuiggiMendoza hmm 是的,我想这真的没有意义,每个东西都有自己的地图。
  • 你应该编辑你的答案并使用另一个有容器的类。这是Map 用法的错误示例
【解决方案2】:

如果您想拥有一组独特的对象,请使用Set 而不是List。

此外,如果您想在对象被视为相等时自行定义,请考虑覆盖该类的 equals 和 hashCode 方法。

【讨论】:

  • 这对于equal() 函数的一般语义来说是不合理的。
  • new Thing("Star Wars", 3) 和 new Thing("Star Wars", 1) 是两个不同的对象。
  • 我同意 Hunter 的评论,如果您创建了 Set 类型的 Thing,那么“星球大战”3 和“星球大战”1 不会被视为唯一对象吗?
  • 您可以重写 equals() 方法以强制它像这样工作,但正如文森特提到的那样,这违反了 equals() 的一般合同。
【解决方案3】:

恕我直言,使用Map&lt;String, Integer&gt; 而不是ArrayList 或Map&lt;String, Thing&gt; 更有意义,如果您不想更改班级。

【讨论】:

    【解决方案4】:

    您需要以这种方式覆盖您的类 Thing 中的 equals 方法和 hashCode 方法:

    public class Thing {
            private String name;
            private Integer num;
    
            public Thing(String a, Integer b) {
                name = a;
                num = b;
            }
    
            public void setName(String name) {
                this.name = name;
            }
    
            public void setNum(Integer num) {
                this.num = num;
            }
    
            @Override
            public boolean equals(Object obj) {
                if(this == obj){
                    return true;
                }
    
                if((obj == null) || (obj.getClass() != this.getClass())){
                    return false;
                }
    
                Thing that = (Thing)obj;
    
                // Use the equality == operator to check if the argument is the reference to this object,
                // if yes. return true. This saves time when actual comparison is costly.
                return  num == that.num &&
                        (name == that.name || (name != null && name.equals(that.name)));
    
            }
    
            /**
             * This method returns the hash code value for the object on which this method is invoked.
             * This method returns the hash code value as an integer and is supported for the benefit of
             * hashing based collection classes such as Hashtable, HashMap, HashSet etc. This method must
             * be overridden in every class that overrides the equals method.
             *
             * @return
             */
            @Override
            public int hashCode() {
                int hash = 7;
                hash = 31 * hash + num;
                hash = 31 * hash + (null == name ? 0 : name.hashCode());
                return hash;
            }
        }
    

    那么你可以这样使用它:

    ArrayList<Thing> myList = new ArrayList<>();
    
    Thing first = new Thing("Star Wars", 3);
    
    if(!myList.contains(first)){
       myList.add(first);
    }
    
    Thing second = new Thing("Star Wars", 1);
    
    if(!myList.contains(second)){
       myList.add(second);
    }
    

    在我的例子中,我使用 LinkedHashSet 来维护插入顺序,因为我认为这会更有效率。我没有用 ArrayList 尝试这个例子。

    有关更多信息,您可以从这里阅读:why I override equals and hashCode in this way

    【讨论】:

      【解决方案5】:

      如果你想最终使用 List 而不是写你的Comparator,

      通过编写 Comparator,您可以创建类似 set 的行为。

      【讨论】:

      • 比较器将如何解决他的问题,当ArrayList#add 只是在内部数组的末尾插入元素?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-29
      • 2012-02-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多