【问题标题】:How to remove duplicates from a list based on a custom java object not a primitive type?如何根据自定义 java 对象而不是原始类型从列表中删除重复项?
【发布时间】:2012-04-27 23:03:37
【问题描述】:

在我发布这个问题之前,我发现here 发布了类似的问题。但答案是基于一个字符串。但是,我在这里有不同的情况。我不是要删除字符串,而是要删除另一个名为 AwardYearSource 的对象。这个类有一个叫做 year 的 int 属性。所以我想根据年份删除重复项。即如果不止一次提到 2010 年,我想删除该 AwardYearSource 对象。我怎样才能做到这一点?

【问题讨论】:

标签: java list arraylist set


【解决方案1】:

根据字段移除元素的最简单方法如下(保留顺序):

Map<Integer, AwardYearSource> map = new LinkedHashMap<>();
for (AwardYearSource ays : list) {
  map.put(ays.getYear(), ays);
}
list.clear();
list.addAll(map.values());

【讨论】:

  • 谢谢。它解决了我的问题。但是我将代码的第一行更改为 Map map = new LinkedHashMap(); ....否则它不会编译。
  • 优秀的家伙...,
  • 又好又简单的方法...超级
【解决方案2】:

另一种方法是为您的对象覆盖hashCode() 和equals(Object obj)。由于它只有一个字段用于确定相等性,因此非常简单。比如:

public boolean equals(Object obj) {
  if (obj == null || !(obj instanceof AwardYearSource)) {
    return false;
  }
  return (this.year == ((AwardYearSource)obj).year);
}
public int hashCode() {
  return this.year;
}

然后您可以将所有对象粘贴到 Set 中以删除重复项:

Set<AwardYearSource> set = new Set<AwardYearSource>();

set.add(new AwardYearSource(2011));
set.add(new AwardYearSource(2012));
set.add(new AwardYearSource(2011));

for (AwardYearSource aws : set) {
  System.out.println(aws.year);
}

【讨论】:

    【解决方案3】:

    相当简单。尽管地图版本让我有些烦恼(并不是我怀疑它们会起作用,但不知何故,这似乎有点矫枉过正——尽管这个版本在这方面不一定更好)。
    答案是功能性的,并且是线程安全的(假设 AwardYearSource 是不可变的)。

    public static List<AwardYearSource> removeDuplicateYears(
                                              final Collection<AwardYearSource> awards) {
        final ArrayList<AwardYearSource> input = new ArrayList<AwardYearSource>(awards);
        // If there's only one element (or none), guaranteed unique.
        if (input.size() <= 1) {
            return input;
        }
        final HashSet<Integer> years = new HashSet<Integer>(input.size(), 1);
        final Iterator<AwardYearSource> iter = input.iterator();
        while(iter.hasNext()) {
            final AwardYearSource award = iter.next();
            final Integer year = award.getYear();
            if (years.contains(year)) {
                iter.remove();
            } else {
                years.add(year);
            }
        }
        return input;       
    
    }
    

    【讨论】:

      【解决方案4】:

      您可以使用地图并以年份为键存储对象:

      Map<Integer, AwardYearSource> map = new HashMap<Integer, AwardYearSource>();
      map.put(someAwardYearSource1.getYear(), someAwardYearSource1);
      map.put(someAwardYearSource2.getYear(), someAwardYearSource2);
      
      etc.
      

      最后,地图将包含按年份的唯一值,您可以使用 values 方法调用:

      Collection<AwardYearSource> noDups = map.values();
      

      【讨论】:

        【解决方案5】:

        创建一个 HashMap 对象,其中 int 作为键类型,您的类作为值类型。然后遍历列表并将每个元素插入到地图中:

        mymap.put(source.year, source);
        

        然后从原始列表中删除所有元素并遍历地图并将每个元素插入到列表中。

        【讨论】:

        • 真的吗?您能否提供更多详细信息?尽管至少有一个迭代器是不必要的 - 请参阅其他一些答案。如果在线程上下文中使用,这会产生一些令人讨厌的副作用。
        • 也许您在错误答案上发表了此评论?我在我的解决方案中没有看到任何迭代器,而且它是线程安全的。
        • 您明确提到了迭代,并且您将使用至少一个 implicit(编译器生成,在 for-each 构造的情况下)迭代器。如果这是在它自己的方法中打包(应该是),那么从原始列表中删除项目是绝对不是线程安全的。
        • 我不认为线程安全在这里是一个相当大的问题,至少根据所提出的问题。我认为我和其他人提供的基于地图的解决方案是最简单的。
        【解决方案6】:

        如果您的 AwardYearSource 类覆盖了 equals 和 hashcode 方法(Eclipse 可以同时生成这两种方法),那么您可以将它们添加到 Set。该集合将不包含任何重复项。

        public class AwardYearSource
        {
            private final int year;
        
            public AwardYearSource(int year)
            {
                this.year = year;
            }
        
            @Override
            public int hashCode()
            {
                final int prime = 31;
                int result = 1;
                result = prime * result + year;
                return result;
            }
        
            @Override
            public boolean equals(Object obj)
            {
                if (this == obj)
                    return true;
                if (obj == null)
                    return false;
                if (getClass() != obj.getClass())
                    return false;
                AwardYearSource other = (AwardYearSource) obj;
                if (year != other.year)
                    return false;
                return true;
            }
        
            @Override
            public String toString()
            {
                return String.valueOf(year);
            }
        
        
            public static void main(String[] args)
            {
                Set<AwardYearSource> set = new HashSet<AwardYearSource>();
                set.add(new AwardYearSource(2000));
                set.add(new AwardYearSource(2000));
                set.add(new AwardYearSource(2000));
                set.add(new AwardYearSource(2000));
        
                System.out.println(set);
            }
        }
        

        输出为 [2000]。集合中只有一项。

        【讨论】:

          【解决方案7】:
          Set<Integer> set = new HashSet<>();
          list.removeIf(i -> set.contains(i.getYear()) ? true : !set.add(i.getYear()));
          

          这应该有所帮助,在这种情况下,重复是根据某些属性(或属性组合)决定的,在这种情况下是年份。希望这会有所帮助。

          【讨论】:

            猜你喜欢
            • 2016-10-15
            • 1970-01-01
            • 1970-01-01
            • 2015-09-12
            • 2021-01-24
            • 1970-01-01
            • 2018-11-25
            • 2015-08-06
            • 1970-01-01
            相关资源
            最近更新 更多