【问题标题】:Remove duplicate in ArrayList of Custom objects删除自定义对象的 ArrayList 中的重复项
【发布时间】:2015-02-22 12:06:09
【问题描述】:

我正在尝试从我的数组中删除重复的对象。

我有我的自定义,它由两个双精度:x 和 y。

我想要做的是删除重复的 ( (x && y) == (x1 && y1)) 并且如果 x == x1 我想保留具有更高 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)]; 

我尝试实现 equals 方法,但我不知道如何使用它:

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

列表总是使用 Collections.sort by x 排序的。

谢谢大家。

【问题讨论】:

  • 通常,x == x+1 永远不会是真的。您的意思可能是 x == x1 并将其与您的列表中具有相同 x 值的条目连续存储的事实混为一谈。
  • 现在,如果您在 x 和 y 上正确排序您的项目,问题就变得微不足道了。
  • 与其覆盖equals(Object other),不如创建一个Comparator

标签: java android algorithm sorting arraylist


【解决方案1】:

像这样给MyObject

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

您可以实现一个unique 方法,它只返回一个包含唯一元素的列表:

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

并对其进行单元测试以验证其是否有效:

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

注意:实现 equalshashCode 很重要,这样才能正常工作, 因为使用了哈希映射。但是无论如何你都应该在你的自定义类中这样做:提供适当的equalshashCode 实现。顺便说一句,我没有写那些 equalshashCode 方法。我让我的 IDE (IntelliJ) 从类的 xy 字段中自动生成它们。

【讨论】:

    【解决方案2】:

    确保在您的自定义 Object(MyObject) 中覆盖 equals() 方法。

    然后将它们添加到Set。现在您有了独特的结果集。

    【讨论】:

      【解决方案3】:

      使用Set 而不是List ...

      您必须覆盖equals()hashCode()。大多数 IDE 都可以为您生成!

      要将列表“转换”为集合,您可以简单地使用:

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

      那么你就有了一个包含独特元素的集合。您可以像这样遍历集合:

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

      【讨论】:

      • 好的,我知道我需要实现equals和hashcode,但是接下来我该怎么做呢?
      • 我已经更新了我的答案。澄清一下:你必须为你的MyObject 类覆盖equals()hashCode()
      【解决方案4】:

      最好的解决方案是使用Set。但是,Java 中有两个Set 实现:HashSetTreeSetHashSet 要求您声明 equalshashCode 方法,而 TreeSet 要求您的类使用 compareTo 方法实现 Comparable 或提供 Comparator。这两种解决方案都不适用于您的情况,因为您希望在x 相等时保持较高的y。如果您基于xy 排序/计算相等性,您将有重复的x,如果您仅基于x 排序/计算相等性,您将获得第一个输入的x,这不是什么你想要的。

      因此,我们需要做的是:

      1. 按 x 升序、y 降序排序
      2. 转换为保留原始顺序的Set,但仅基于x 的相等性
      3. 转换回列表(如有必要)

      XAscYdesc 比较器方法(不考虑空值):

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

      XAsc 比较器方法(不考虑空值):

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

      (使用 Guava 库;对于这样的单行代码非常有用):

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

      【讨论】:

        【解决方案5】:

        您需要在xy 上订购您的收藏品,并首先使用x(将其想象为 xy 形成一个假设的数字x 在小数点左侧,y 在小数点左侧:按递增顺序对数字进行排序时,如果整数部分相等,则按小数部分)。 现在,使用equal 谓词,将很难对值进行排序(您只能判断它们是否相等,而不能判断一个是否在另一个之前)。相反,您需要使用以下方法实现comparable 接口:

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

        如果你的数组是根据这个比较排序的,你只需要在你的数组中保留最后一个具有相同x 的条目就可以得到你想要的。这意味着您删除条目除非其后继有不同的x

        这个方法很有趣,你不想保留原始数据,而只保留结果:它将更新现有数组,复杂度为 O(n)时间(不计算排序,如果我正确理解您的问题,无论如何都会发生)。


        或者,整个过滤可以通过在您的集合上应用 fold 来实现,这里的折叠只是为相同的x 保持最高的y(正如您在你的问题)。因为您的集合已经在x 上排序,这意味着它自然地按x 的值进行分区,因此您可以通过累积为每个分区正确的x 构建一个新集合另一个数组。对于数组的每个元素,将其与新数组中最后插入的条目进行比较,如果x 相同,则将其替换为具有最高y 的对象。如果x 不同,则将其添加到新数组中。

        这种方法的优点是您不需要更改排序算法,但另一方面您需要一个新数组。因此,该算法应该在 O(n) 空间和时间中工作。

        最后,该算法可能适用于原始数组的就地更新。它稍微复杂一些,但可以让您避免额外的分配(对嵌入式系统至关重要)。

        伪代码:

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

        请注意,在恒定空间中工作时,由于ArrayList 在内部工作的方式,它可能比分配算法效率略低(尽管它应该比使用其他常用容器类型更好)。

        【讨论】:

          【解决方案6】:
          /**
           * 
           */
          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;
              }
          
          }
          

          【讨论】:

          • 在回答问题时,最好解释得更详细,以便网站的其他用户更容易理解。
          猜你喜欢
          • 2015-07-10
          • 2015-12-22
          • 2018-12-15
          • 1970-01-01
          • 2016-06-17
          • 1970-01-01
          • 2013-12-24
          • 1970-01-01
          • 2017-01-11
          相关资源
          最近更新 更多