如何遍历Person对象的ArrayList,仅将不重复name的对象存入新列表?
解决ArrayList按name去重的问题
你的思路方向是对的,但代码里有几个关键问题导致重复项没被彻底处理:
- 字符串比较错误:用
p1.getName() == p2.getName()是比较对象引用,不是字符串内容,应该用p1.getName().equals(p2.getName())(如果name可能为null,还要额外加null判断)。 - 迭代器与原列表不同步:外层用Iterator遍历,内层直接修改原列表(设为null),但Iterator是基于初始列表状态创建的,不会自动感知原列表的修改,导致有些重复项还是会被迭代到。
- 逻辑冗余且易出错:内层遍历整个原列表并设null的操作效率低,还容易因为
indexOf在列表存在null时的异常行为,导致漏处理重复项。
下面给你几个更简洁可靠的方案:
方案1:用Set跟踪已存在的name(最直观高效)
用一个HashSet<String>记录已经添加到solution的name,遍历原列表时,只要当前Person的name不在Set里,就添加到solution并把name存入Set:
ArrayList<Person> solution = new ArrayList<>(); HashSet<String> existingNames = new HashSet<>(); for (Person p : persons) { if (p != null && p.getName() != null && !existingNames.contains(p.getName())) { solution.add(p); existingNames.add(p.getName()); } }
这个方法时间复杂度是O(n),比你原来的O(n²)高效很多,逻辑清晰,完全避开了迭代器同步的问题。
方案2:Java 8+ 流处理(代码更简洁)
如果用Java 8及以上版本,用Stream可以写出更简洁的去重代码:
方式A:利用Set的add特性做过滤
HashSet<String> existingNames = new HashSet<>(); ArrayList<Person> solution = persons.stream() .filter(p -> p != null && p.getName() != null && existingNames.add(p.getName())) .collect(Collectors.toCollection(ArrayList::new));
这里利用了HashSet.add()的特性:元素不存在时添加并返回true,存在则返回false,刚好可以作为filter的判断条件。
方式B:用Collectors.toMap实现去重
ArrayList<Person> solution = new ArrayList<>( persons.stream() .filter(p -> p != null && p.getName() != null) .collect(Collectors.toMap( Person::getName, Function.identity(), (existing, replacement) -> existing // 重复时保留第一个出现的对象 )).values() );
这个方法把列表转成以name为key的Map,重复的key会保留第一个出现的Person对象,最后取Map的values转成ArrayList即可。
方案3:重写Person的equals和hashCode(适合特定业务场景)
如果你的业务逻辑中,name相同的Person就视为同一个对象,可以重写Person类的equals和hashCode方法,再用HashSet去重:
// 先在Person类中重写equals和hashCode @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return Objects.equals(name, person.name); // 仅以name判断相等性 } @Override public int hashCode() { return Objects.hash(name); } // 然后执行去重 ArrayList<Person> solution = new ArrayList<>(new HashSet<>(persons));
注意:这个方案会改变Person对象的相等性判断逻辑,如果你的业务中Person的相等性需要结合age、birthday等其他属性,就不要用这个方法。
你的原代码核心问题是通过修改原列表来标记重复项,但迭代器无法同步感知这种修改,导致重复项漏处理。上面的方案都避开了修改原列表,而是通过跟踪已存在的name来实现去重,更可靠也更高效。
内容的提问来源于stack exchange,提问作者Borat
相关产品推荐
相关产品推荐

