Prevent duplicate entries in arraylist
Asked Answered
H

5

7

Say I create some object class like so

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

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

Now I want to create an arraylist to hold a number of this object class like so.

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

I would like to include some sort of logic so that in this case...when we try and add object "second" rather than add a new object to the arrayList, we add second.getNum() to first.getNum(). So if you were to iterate through the ArrayList it would be

"Star Wars", 4

I am having trouble coming up with an elegant way of handling this. And as the arraylist grows, searching through it to determine if there are duplicate name items becomes cumbersome. Can anyone provide some guidance on this?

Heliogravure answered 1/4, 2012 at 5:16 Comment(2)
It looks like you want to use a java.util.Map instead.Kolyma
I think the best approach would be to implement a "no duplicates" array list. See my answer here: https://mcmap.net/q/210391/-is-there-a-no-duplicate-list-implementation-out-thereGaud
D
4

You would have to create your own method to check to see if the the name field of class Thing was set to "Star Wars" then add to the corresponding num field of Class Thing, that is one possible solution.

Another solution is to use a Map with the name field as the key, and the num field as the value.

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

}

Hope this helps.

Duly answered 1/4, 2012 at 5:30 Comment(5)
it doesn't make sense to putthe map in the Thing classIso
@LuiggiMendoza It's just an example. The op can put the name wherever they want.Duly
if Thing is a model class, why should you put a container that only stores data of the actual object?Iso
@LuiggiMendoza hmm Yeah I guess it really doesn't make sense, each thing would have its own map.Duly
you should edit your answer and use another class which has the container. This is a bad sample of the Map usageIso
B
4

If you want to have a set of unique objects, use Set instead of List.

Also, if you want to define by yourself when objects are considered equal, consider overriding the equals and hashCode methods of the class.

Brigidbrigida answered 1/4, 2012 at 5:18 Comment(4)
That would not be rational with the general semantic of the equal() function.Acanthoid
new Thing("Star Wars", 3) and new Thing("Star Wars", 1) are two different objects.Duly
I agree with Hunter's comment, if you created a Set of type Thing then wouldn't "Star Wars", 3 and "Star Wars", 1 be considered as unique objects?Heliogravure
You could override the equals() method to force it to work like this, but as Vincent mentions this violates the general contract of equals().Duly
D
4

You would have to create your own method to check to see if the the name field of class Thing was set to "Star Wars" then add to the corresponding num field of Class Thing, that is one possible solution.

Another solution is to use a Map with the name field as the key, and the num field as the value.

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

}

Hope this helps.

Duly answered 1/4, 2012 at 5:30 Comment(5)
it doesn't make sense to putthe map in the Thing classIso
@LuiggiMendoza It's just an example. The op can put the name wherever they want.Duly
if Thing is a model class, why should you put a container that only stores data of the actual object?Iso
@LuiggiMendoza hmm Yeah I guess it really doesn't make sense, each thing would have its own map.Duly
you should edit your answer and use another class which has the container. This is a bad sample of the Map usageIso
A
1

IMHO, it makes more sense to use a Map<String, Integer> instead of the ArrayList, or a Map<String, Thing> if you don't want to change your class.

Acanthoid answered 1/4, 2012 at 5:23 Comment(0)
S
1

You need to override the equals method and hashCode method in your class Thing in this way:

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

Then you could use it in this way:

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

In my case I'm using LinkedHashSet to maintain the order of insertion and because I think is going to be more efficient. I didn't try this example with ArrayList.

For more information you can read from here: why I override equals and hashCode in this way

Splenomegaly answered 20/10, 2015 at 13:37 Comment(0)
Q
0

If you want to use List finally than Write your Comparator,

by writing Comparator you can create behaviour like set.

Quasi answered 1/4, 2012 at 5:20 Comment(1)
how comparator will solve his problem, when ArrayList#add just insert the element at the end of the inner array?Iso

© 2022 - 2024 — McMap. All rights reserved.