Предотвратить дублирование записей в arraylist

Скажем, я создаю какой-то объектный класс, например так

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.getNum() к first.getNum(). Итак, если бы вы перебирали ArrayList, это было бы

"Star Wars", 4

У меня возникли проблемы с придумыванием элегантного способа справиться с этим. И по мере роста массива поиск по нему, чтобы определить, есть ли повторяющиеся элементы имени, становится громоздким. Может ли кто-нибудь дать некоторые рекомендации по этому поводу?


person tyh    schedule 01.04.2012    source источник
comment
Похоже, вы хотите вместо этого использовать java.util.Map.   -  person Nayuki    schedule 01.04.2012
comment
Я думаю, что лучшим подходом было бы реализовать список массивов без дубликатов. Смотрите мой ответ здесь: stackoverflow.com/a/35788695/130028   -  person marcolopes    schedule 13.03.2016


Ответы (5)


Вам нужно будет создать свой собственный метод, чтобы проверить, установлено ли в поле name класса Thing значение «Звездные войны», а затем добавить в соответствующее поле num класса Thing, это одно из возможных решений.

Другое решение — использовать Map с полем имени в качестве ключа и полем num в качестве значения.

ex:

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());
   }

}

Надеюсь это поможет.

person Hunter McMillen    schedule 01.04.2012
comment
нет смысла помещать карту в класс Thing - person Luiggi Mendoza; 01.04.2012
comment
@LuiggiMendoza Это просто пример. Оператор может поставить имя где угодно. - person Hunter McMillen; 01.04.2012
comment
если Thing - это класс модели, зачем вам помещать контейнер, который хранит только данные фактического объекта? - person Luiggi Mendoza; 01.04.2012
comment
@LuiggiMendoza хм Да, я думаю, это действительно не имеет смысла, у каждой вещи будет своя карта. - person Hunter McMillen; 01.04.2012
comment
вы должны отредактировать свой ответ и использовать другой класс, у которого есть контейнер. Это плохой пример использования Map - person Luiggi Mendoza; 01.04.2012

Если вы хотите иметь набор уникальных объектов, используйте Set вместо List.

Кроме того, если вы хотите самостоятельно определить, когда объекты считаются равными, рассмотрите возможность переопределения методов equals и hashCode класса.

person MByD    schedule 01.04.2012
comment
Это было бы неразумно с общей семантикой функции equal(). - person Vincent Cantin; 01.04.2012
comment
new Thing("Star Wars", 3) и new Thing("Star Wars", 1) — это два разных объекта. - person Hunter McMillen; 01.04.2012
comment
Я согласен с комментарием Хантера, если бы вы создали Набор типа Вещь, то разве «Звездные войны, 3» и «Звездные войны, 1» не считались бы уникальными объектами? - person tyh; 01.04.2012
comment
Вы можете переопределить метод equals(), чтобы заставить его работать так, но, как упоминает Винсент, это нарушает общий контракт equals(). - person Hunter McMillen; 02.04.2012

ИМХО, имеет смысл использовать Map<String, Integer> вместо ArrayList или Map<String, Thing>, если вы не хотите менять свой класс.

person Vincent Cantin    schedule 01.04.2012

Вам нужно переопределить метод equals и метод hashCode в вашем классе Thing следующим образом:

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.

Для получения дополнительной информации вы можете прочитать здесь: почему я переопределяю equals и hashCode таким образом

person Jorge Casariego    schedule 20.10.2015

Если вы хотите использовать List наконец, чем Write your Comparator,

написав Comparator, вы можете создать поведение, подобное set.

person Yogesh Prajapati    schedule 01.04.2012
comment
как компаратор решит свою проблему, когда ArrayList#add просто вставит элемент в конец внутреннего массива? - person Luiggi Mendoza; 01.04.2012