Remove duplicate to ArrayList from custom objects

I am trying to remove duplicate objects from my array.

I have my own custom, which consists of two doubles: x and y.

What I want to do is remove the duplicate ((x && y) == (x1 & y1)), and if x == x1, I want to keep the object with a higher y.

ArrayList<MyObject> list = [x(0),y(0)], [x(0),y(0)], [x(0.5),y(0.5], [x(0.5),y(0.6)], [x(1),y(1)]; 
ArrayList<MyObject> results = [x(0),y(0)], [x(0.5),y(0.6)], [x(1),y(1)]; 

      

I tried to implement the equals method, but I am not using it:

public boolean equals(Object obj) {
    if (obj == null || !(obj instanceof MyObject)) {
        return false;
    }
    return (this.x == ((MyObject)obj).x);
}

      

List

always ordered using Collections.sort by x.

Thanks everyone.

+3


source to share


6 answers


Considering MyObject

as follows:

class MyObject {
    private final double x;
    private final double y;

    public MyObject(double x, double y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        MyObject myObject = (MyObject) o;

        if (Double.compare(myObject.x, x) != 0) return false;
        if (Double.compare(myObject.y, y) != 0) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        temp = Double.doubleToLongBits(x);
        result = (int) (temp ^ (temp >>> 32));
        temp = Double.doubleToLongBits(y);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        return result;
    }
}

      

You can implement a method unique

that returns a list with only unique elements:

private List<MyObject> unique(List<MyObject> list) {
    List<MyObject> uniqueList = new ArrayList<>();
    Set<MyObject> uniqueSet = new HashSet<>();
    for (MyObject obj : list) {
        if (uniqueSet.add(obj)) {
            uniqueList.add(obj);
        }
    }
    return uniqueList;
}

      



And a unit test to check:

@Test
public void removeDups() {
    List<MyObject> list = Arrays.asList(new MyObject(0, 0), new MyObject(0, 0), new MyObject(0.5, 0.5), new MyObject(0.5, 0.6), new MyObject(1, 1));
    List<MyObject> results = Arrays.asList(new MyObject(0, 0), new MyObject(0.5, 0.5), new MyObject(0.5, 0.6), new MyObject(1, 1));
    assertEquals(results, unique(list));
}

      

Note: It is important for this to implement both equals

and hashCode

for this to work, due to the use of a hash map. But you should always do this in your custom classes: provide appropriate implementations equals

and hashCode

. By the way, I did not write these methods equals

and hashCode

. I let my IDE (IntelliJ) generate them automatically from fields x

and y

for a class.

+6


source


Make sure to override the method equals()

on your custom object ( MyObject

).



Then add them to Set

. You now have a unique result set.

0


source


Use Set

instead List

...

You must override equals()

and hashCode()

. Most IDEs can generate this for you!

To "convert" a list to a set, you can simply use this:

ArrayList<MyObject> list =  ...
Set<MyObject> mySet = new HashSet<MyObject>(list);

      

Then you have a set with unique elements. You can iterate over the set like this:

for (MyObject o : mySet){
   o.getX();
}

      

0


source


First you need to order your collection in both c x

and y

c x

(think of it as x and y, forming a hypothetical number with x on the left side of the decimal point and y on the right side: when you sort a number in ascending order, if the integral parts are equal, you are sorting by decimal). Now, with a predicate equal

, it will be difficult to sort the values ​​(you can only tell if they are equal, not if they are in front of another). Instead, you need to implement the interface comparable

with the following method:

public int compare(Object obj) {
  if (obj == null || !(obj instanceof MyObject)) {
    // raise an Exception
  }
  MyObject other = (MyObject)obj;
  if (x < other.x) return -1;
 if (this.x > other.x) return 1;
  if (this.y < other.y) return -1;
  if (this.y > other.y) return 1;
  return 0;
}

      

If your array is sorted according to this comparison, you just need to keep the last entry with the same x

in your array to get what you want. This means that you delete records if its successor has no other x

.

This method is interesting because you don't want to store the original data, but you keep the result: it updates the existing array in place, for O (n) complexity in time (not counting the sorting, which should happen anyway if I understood your question correctly ).


Alternatively, all filtering can be achieved by applying a crease to your collection, where the folding here just keeps the highest y

for the same thing x

(as you exactly stated in your question). Since your collection is already sorted by x

, this means that it is naturally split into values x

, so you can build a new collection by accumulating the correct one x

for each section in a different array. For each element of the array, you compare it with the last inserted entry in the new array, if they x

match, you replace it with the object with the highest y

. If the parameter x

is different, you add it to a new array.

The advantage of this approach is that you don't need to change the sorting algorithm, but on the other hand, you need a new array. Therefore, this algorithm should work in O (n) space and time.

Finally, this algorithm can be adapted to update the original array in place. This is a little more complicated, but will allow you to avoid additional allocation (which is important for embedded systems).

Pseudocode:

int idx = 0;
while (idx + 1 < Objarray.size()){
  MyObj oi = Objarray.get(idx), on = Objarray.get(idx+1);
  if (oi.x == on.x) {
    if (on.y < oi.y) 
      Objarray.remove(idx++);
    else
      Objarray.remove(idx+1);
  } else
    idx++;
}

      

Note that when working in constant space, it may be slightly less efficient than the allocation algorithm, as the method ArrayList

works internally (although it should be better than using other common container types).

0


source


The most optimal solution would be to use Set

. However, Java has two implementations Set

: HashSet

and TreeSet

. HashSet

requires you to declare methods equals

and hashCode

, but TreeSet

requires your class to implement Comparable

with a method compareTo

or supply Comparator

. Neither solution will work in your case, because you want to keep the higher y

when x

equal. If you sort / calculate equality based on x

, y

you will have a duplicate x

, and if you sort / calculate equality based on x

, you only get the first one x

, which is not something you want.

Therefore, we need to do the following:

  • Sort x ascending, descending
  • Convert to Set

    , which preserves the original ordering but bases equality only onx

  • Convert back to list (if needed)

Comparator method XAscYdesc (excluding zeros):

public int compare(MyObject left, MyObject right) {
  int c = left.x - right.x;
  if(c != 0) {
    return c;
  }
  return right.y - left.y;
}

      

XAsc comparator method (excluding zeros):

public int compare(MyObject left, MyObject right) {
  return left.x - right.x;
}

      

(Using the Guava library it is very useful for one line files):

Collections.sort(list, new XAscYdesc());
Lists.newArrayList(ImmutableSortedSet.copyOf(new XAsc(), list));

      

0


source


/**
 * 
 */
package test1;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author raviteja
 *
 */
public class UinquecutomObjects {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Employee e1=new Employee();
        e1.setName("abc");
        e1.setNo(1);

        Employee e2=new Employee();
        e2.setName("def");
        e2.setNo(2);

        Employee e3=new Employee();
        e3.setName("abc");
        e3.setNo(1);

        List<Employee> empList=new ArrayList<Employee>();
        empList.add(e1);
        empList.add(e2);
        empList.add(e3);

        System.out.println("list size is "+empList.size());

        Set<Employee> set=new HashSet<Employee>(empList);

        System.out.println("set size is "+set.size());
        System.out.println("set elements are  "+set);



    }

}


class Employee{

    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getNo() {
        return no;
    }
    public void setNo(int no) {
        this.no = no;
    }
    private int no;
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        result = prime * result + no;
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Employee other = (Employee) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        if (no != other.no)
            return false;
        return true;
    }

}

      

0


source







All Articles