4

i'm trying to remove duplicate objects from my array.

I have my custom which is made of two double : x and y.

What i want to do is to remove duplicate ( (x && y) == (x1 && y1)) and if x == x1 i want to keep the object which has the 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 do not how to use it :

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

list is always ordered using Collections.sort by x.

Thanks for all.

3
  • Usually, x == x+1 is never true. You probably meant x == x1 and mixed it up with the fact that in your list, entries with the same x value are stored consecutively. Commented Dec 24, 2014 at 11:03
  • Now if you sort your items properly on both x and y, the problem becomes trivial. Commented Dec 24, 2014 at 11:06
  • Rather than overriding equals(Object other), create a Comparator. Commented Dec 24, 2014 at 11:43

6 Answers 6

8

Given MyObject like this:

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 unique method that returns a list with unique elements only:

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 for it to verify it works:

@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's important to implement both equals and hashCode for this to work, because of the use of a hash map. But you should always do this anyway in your custom classes: provide appropriate equals and hashCode implementations. Btw, I didn't write those equals and hashCode methods. I let my IDE (IntelliJ) generate them automatically from the fields x and y of the class.

Sign up to request clarification or add additional context in comments.

Comments

1

Make sure to override equals() method in your custom Object(MyObject).

Then add them into a Set. Now you have unique result set.

Comments

0

Use a Set instead of a List ...

You have to override equals() and hashCode(). The most IDE can generate that 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();
}

2 Comments

ok i get it that i need to implement equals and hashcode, but next what do i do with this ?
I have updated my answer. Just to clarify: you have to override equals() and hashCode() for your MyObject class
0

The most optimal solution would be if you could use a Set. However, there are two Set implementations in Java: HashSet and TreeSet. HashSet requires that you declare equals and hashCode methods, while TreeSet requires your class to implement Comparable with a compareTo method or supply a Comparator. Neither solution will work in your case because you want to keep the higher y when x is equal. If you sort/calculate equality based on x, y you will have duplicate x, and if you sort/calculate equality based on x only you will get the first x entered, which is not what you want.

Therefore, what we need to do is:

  1. Sort by x ascending, y descending
  2. Convert to Set that preserves original order, but bases equality only on x
  3. Convert back to list (if necessary)

XAscYdesc Comparator Method (Not accounting for nulls):

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 (Not accounting for nulls):

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

(Using the Guava library; it's very useful for one-liners like this):

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

Comments

0

You need to order your collection on both x and y with x first (think about it as x and y forming an hypothetical number with x on the left side of the decimal point, and y on the right side: when you sort number in growing order, if the integral parts are equal, you sort on the decimal part). Now, with an equal predicate, it will be difficult to sort values (you can only tell if they are equal, not if one is before another). Instead, you need to implement the comparable interface, 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 remove entries unless its successor has a different x.

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


Alternatively, the whole filtering can be achieved by applying a fold on your collection, where folding here is simply keeping the highest y for a same x (as you stated it precisely in your question). Because your collection is already sorted on x, it means that it is naturally partitioned on values of x, so you can build a new collection by accumulating the correct x for each partition in another array. For each element of the array, you compare it with the last inserted entry in your new array, if the x are the same, you replace it with the object with the highest y. If the x are different, you add it to the 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. This algorithm should therefore work in O(n) space and time.

Finally, this algorithm may be adapted to an in place update of the original array. It is slightly more complicated, but would let you avoid the extra allocation (crucial on embedded systems).

Pseudo code:

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 while working in constant space, it might be slightly less efficient than the allocating algorithm, because of the way ArrayList works internally (though it should be better than using other usual container types).

Comments

0
/**
 * 
 */
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;
    }

}

1 Comment

When answering questions it is best to explain in more detail so that it can be more easily understood by other users of the site.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.