Как удалить повторяющиеся объекты в List <MyObject> без equals/hashcode?
Я должен удалить дубликаты объектов в списке. Это список из объекта Blog, который выглядит следующим образом:
public class Blog {
private String title;
private String author;
private String url;
private String description;
...
}
Дублированный объект - это объект, у которого заголовок, автор, URL и описание совпадают с другим объектом.
И я не могу изменить объект. Я не могу поставить новые методы на это.
Как мне это сделать?
Ответы
Ответ 1
Если вы не можете отредактировать исходный код класса (почему бы и нет?), вам нужно перебрать список и сравнить каждый элемент на основе четырех упомянутых критериев ( "title, author, url and description" ).
Для этого я создам новый класс, например BlogKey
, который содержит эти четыре элемента и , который правильно реализует equals()
и hashCode()
. Затем вы можете перебрать исходный список, построив BlogKey
для каждого и добавив к HashMap
:
Map<BlogKey, Blog> map = new HashMap<BlogKey, Blog>();
for (Blog blog : blogs) {
BlogKey key = createKey(blog);
if (!map.containsKey(key)) {
map.put(key, blog);
}
}
Collection<Blog> uniqueBlogs = map.values();
Однако самое простое - просто отредактировать исходный исходный код Blog
, чтобы он правильно реализовал equals()
и hashCode()
.
Ответ 2
Вот полный код, который работает для этого сценария:
class Blog {
private String title;
private String author;
private String url;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
private String description;
Blog(String title, String author, String url, String description)
{
this.title = title;
this.author = author;
this.url = url;
this.description = description;
}
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
if(obj instanceof Blog)
{
Blog temp = (Blog) obj;
if(this.title == temp.title && this.author== temp.author && this.url == temp.url && this.description == temp.description)
return true;
}
return false;
}
@Override
public int hashCode() {
// TODO Auto-generated method stub
return (this.title.hashCode() + this.author.hashCode() + this.url.hashCode() + this.description.hashCode());
}
}
Вот основная функция, которая удалит дубликаты:
public static void main(String[] args) {
Blog b1 = new Blog("A", "sam", "a", "desc");
Blog b2 = new Blog("B", "ram", "b", "desc");
Blog b3 = new Blog("C", "cam", "c", "desc");
Blog b4 = new Blog("A", "sam", "a", "desc");
Blog b5 = new Blog("D", "dam", "d", "desc");
List<Blog> list = new ArrayList();
list.add(b1);
list.add(b2);
list.add(b3);
list.add(b4);
list.add(b5);
//Removing Duplicates;
Set<Blog> s= new HashSet<Blog>();
s.addAll(list);
list = new ArrayList<Blog>();
list.addAll(s);
//Now the List has only the identical Elements
}
Ответ 3
Убедитесь, что Blog
имеет методы equals(Object)
и hashCode()
, и addAll(list)
, затем или new LinkedHashSet()
, если порядок важен.
Еще лучше, используйте Set
вместо List
с самого начала, так как вы, очевидно, не хотите дубликатов, лучше, чтобы ваша модель данных отражала это, а не удаляла их после факта.
Ответ 4
- переопределить
hashCode()
и equals(..)
с помощью этих 4 полей
- use
new HashSet<Blog>(blogList)
- это даст вам Set
, который по умолчанию не имеет дубликатов
Обновление. Поскольку вы не можете изменить класс, здесь решение O (n ^ 2):
- создать новый список
- повторить первый список
- во внутреннем цикле повторите второй список и проверьте, имеет ли он элемент с теми же полями
Вы можете сделать это более эффективным, если вы предоставите структуру данных HashSet
с помощью внешних методов hashCode()
и equals(..)
.
Ответ 5
Используйте set:
yourList = new ArrayList<Blog>(new LinkedHashSet<Blog>(yourList));
Это создаст список без дубликатов, а порядок элементов будет таким же, как в исходном списке.
Просто не забудьте реализовать hashCode() и equals() для вашего класса Blog.
Ответ 6
Вот один из способов удаления повторяющегося объекта.
Класс блога должен быть чем-то вроде этого или подобного, например, правильно pojo
public class Blog {
private String title;
private String author;
private String url;
private String description;
private int hashCode;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public boolean equals(Object obj) {
Blog blog = (Blog)obj;
if(title.equals(blog.title) &&
author.equals(blog.author) &&
url.equals(blog.url) &&
description.equals(blog.description))
{
hashCode = blog.hashCode;
return true;
}else{
hashCode = super.hashCode();
return false;
}
}
}
И используйте его так, чтобы удалить дубликаты объектов. Ключевой структурой данных здесь является Set и LinkedHashSet. Он удалит дубликаты, а также сохранит порядок ввода
Blog blog1 = new Blog();
blog1.setTitle("Game of Thrones");
blog1.setAuthor("HBO");
blog1.setDescription("The best TV show in the US");
blog1.setUrl("www.hbonow.com/gameofthrones");
Blog blog2 = new Blog();
blog2.setTitle("Game of Thrones");
blog2.setAuthor("HBO");
blog2.setDescription("The best TV show in the US");
blog2.setUrl("www.hbonow.com/gameofthrones");
Blog blog3 = new Blog();
blog3.setTitle("Ray Donovan");
blog3.setAuthor("Showtime");
blog3.setDescription("The second best TV show in the US");
blog3.setUrl("www.showtime.com/raydonovan");
ArrayList<Blog> listOfBlogs = new ArrayList<>();
listOfBlogs.add(blog1);
listOfBlogs.add(blog2);
listOfBlogs.add(blog3);
Set<Blog> setOfBlogs = new LinkedHashSet<>(listOfBlogs);
listOfBlogs.clear();
listOfBlogs.addAll(setOfBlogs);
for(int i=0;i<listOfBlogs.size();i++)
System.out.println(listOfBlogs.get(i).getTitle());
Запуск этого должен печатать
Game of Thrones
Ray Donovan
Второй будет удален, потому что это дубликат первого объекта.
Ответ 7
используйте этот код
public List<Blog> removeDuplicates(List<Blog> list) {
// Set set1 = new LinkedHashSet(list);
Set set = new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if (((Blog) o1).get().equalsIgnoreCase(((Blog) o2).getId()) /*&&
((Blog)o1).getName().equalsIgnoreCase(((Blog)o2).getName())*/) {
return 0;
}
return 1;
}
});
set.addAll(list);
final List newList = new ArrayList(set);
return newList;
}
Ответ 8
Если ваш класс Blog
имеет соответствующий метод equals()
, определенный на нем, самый простой способ - просто создать Set
из вашего списка, который автоматически удалит дубликаты:
List<Blog> blogList = ...; // your initial list
Set<Blog> noDups = new HashSet<Blog>(blogList)
Скорее всего, это будет прозрачно работать с остальной частью вашего кода - если вы просто повторяете содержимое, например, то любой экземпляр Collection
не хуже другого. (Если порядок итераций имеет значение, тогда вы можете вместо этого выбрать LinkedHashSet
, который сохранит исходный порядок списка).
Если вам действительно нужен результат List
, а затем, используя простой подход, вы можете просто преобразовать его прямо назад, обернув ArrayList
(или аналогичный). Если ваши коллекции относительно малы (скажем, менее тысячи элементов), то очевидная неэффективность этого подхода, вероятно, будет несущественной.
Ответ 9
Вы можете переопределить метод equals()
с заголовком, автором, URL-адресом и описанием. (и hashCode()
, так как если вы переопределите один, вы должны переопределить другой). Затем используйте HashSet
типа <blog>
.
Ответ 10
Первый шаг, который вам нужен, - реализовать метод equals и сравнить ваши поля. После этого шаги меняются.
Вы можете создать новый пустой список и создать цикл над оригиналом, используя: if (! list2.contains(item)), а затем добавьте.
Еще один быстрый способ сделать это - перебить все их в Set и вернуть их в список. Это работает, потому что наборы не позволяют дублировать файлы.
Ответ 11
И я не могу изменить объект. Я не могу использовать новые методы.
Как это сделать?
Если вы также имеете в виду, как сделать объект неизменным и предотвратить подклассу: используйте ключевое слово final
public final class Blog { //final classes can't be extended/subclassed
private final String title; //final members have to be set in the constructor and can't be changed
private final String author;
private final String url;
private final String description;
...
}
Изменить: я только что видел некоторые ваши комментарии, и, похоже, вы хотите изменить класс, но не можете (третье лицо, которое я предполагаю).
Чтобы предотвратить дубликаты, вы можете использовать оболочку, которая реализует соответствующие equals()
и hashCode()
, а затем использовать аргумент Set
, упомянутый другими:
class BlogWrapper {
private Blog blog; //set via constructor etc.
public int hashCode() {
int hashCode = blog.getTitle().hashCode(); //check for null etc.
//add the other hash codes as well
return hashCode;
}
public boolean equals(Object other) {
//check if both are BlogWrappers
//remember to check for null too!
Blog otherBlog = ((BlogWrapper)other).getBlog();
if( !blog.getTitle().equals(otherBlog.getTitle()) {
return false;
}
... //check other fields as well
return true
}
}
Обратите внимание, что это просто грубая и простая версия и не содержит обязательных нулевых проверок.
Наконец, используйте Set<BlogWrapper>
, пропустите все блоги и попробуйте добавить new BlogWrapper(blog)
в набор. В конце вы должны иметь только уникальные (завернутые) блоги в наборе.
Ответ 12
Я попытался сделать несколько способов удаления дубликатов из списка объектов Java
Некоторые из них: 1. Переопределить методы equals и hashCode и преобразовать список в набор, передав список конструктору класса set и удалить и добавить все. 2. Запустите 2 указателя и удалите дублирует вручную, запустив 2 для циклов один внутри другого, как мы делали на языке C для массивов. 3. Запишите анонимный класс Comparator для bean и выполните команду Collections.sort, а затем запустите 2 указателя для удаления в вперед.
И более того, мое требование заключалось в том, чтобы удалить почти 1 миллион дубликатов из почти 5 миллионов объектов. Итак, после стольких испытаний я закончил третий вариант, который, по моему мнению, является самым эффективным и эффективным способом, и он оказался оценен в секунды, когда другие 2 варианта почти занимают от 10 до 15 минут.
Первый и второй варианты очень неэффективны, потому что, когда мои объекты увеличивают время, затраченное на удаление дубликатов, увеличиваются экспоненциально. вариант лучший.
Ответ 13
import java.util.ArrayList;
import java.util.HashSet;
class Person
{
public int age;
public String name;
public int hashCode()
{
// System.out.println("In hashcode");
int hashcode = 0;
hashcode = age*20;
hashcode += name.hashCode();
System.out.println("In hashcode : "+hashcode);
return hashcode;
}
public boolean equals(Object obj)
{
if (obj instanceof Person)
{
Person pp = (Person) obj;
boolean flag=(pp.name.equals(this.name) && pp.age == this.age);
System.out.println(pp);
System.out.println(pp.name+" "+this.name);
System.out.println(pp.age+" "+this.age);
System.out.println("In equals : "+flag);
return flag;
}
else
{
System.out.println("In equals : false");
return false;
}
}
public void setAge(int age)
{
this.age=age;
}
public int getAge()
{
return age;
}
public void setName(String name )
{
this.name=name;
}
public String getName()
{
return name;
}
public String toString()
{
return "[ "+name+", "+age+" ]";
}
}
class ListRemoveDuplicateObject
{
public static void main(String[] args)
{
ArrayList<Person> al=new ArrayList();
Person person =new Person();
person.setName("Neelesh");
person.setAge(26);
al.add(person);
person =new Person();
person.setName("Hitesh");
person.setAge(16);
al.add(person);
person =new Person();
person.setName("jyoti");
person.setAge(27);
al.add(person);
person =new Person();
person.setName("Neelesh");
person.setAge(60);
al.add(person);
person =new Person();
person.setName("Hitesh");
person.setAge(16);
al.add(person);
person =new Person();
person.setName("Mohan");
person.setAge(56);
al.add(person);
person =new Person();
person.setName("Hitesh");
person.setAge(16);
al.add(person);
System.out.println(al);
HashSet<Person> al1=new HashSet();
al1.addAll(al);
al.clear();
al.addAll(al1);
System.out.println(al);
}
}
вывод
[[Neelesh, 26], [Hitesh, 16], [jyoti, 27], [Neelish, 60], [Hitesh, 16], [Mohan, 56], [Hitesh, 16]]
В hashcode: -801018364
В hashcode: -2133141913
В хэш-коде: 101608849
В hashcode: -801017684
В hashcode: -2133141913
[Hitesh, 16]
Hitesh Hitesh
16 16
В равных: true
В хэш-коде: 74522099
В hashcode: -2133141913
[Hitesh, 16]
Hitesh Hitesh
16 16
В равных: true
[[Neelesh, 60], [Neelash, 26], [Mohan, 56], [jyoti, 27], [Hitesh, 16]]
Ответ 14
Сначала переопределите метод equals()
:
@Override
public boolean equals(Object obj)
{
if(obj == null) return false;
else if(obj instanceof MyObject && getTitle() == obj.getTitle() && getAuthor() == obj.getAuthor() && getURL() == obj.getURL() && getDescription() == obj.getDescription()) return true;
else return false;
}
а затем используйте:
List<MyObject> list = new ArrayList<MyObject>;
for(MyObject obj1 : list)
{
for(MyObject obj2 : list)
{
if(obj1.equals(obj2)) list.remove(obj1); // or list.remove(obj2);
}
}
Ответ 15
Создайте новый класс, который обертывает объект Blog и предоставляет необходимый метод равенства /hashcode. Для максимальной эффективности я бы добавил два статических метода на обертку, один для преобразования списка блогов → Список Wrapper для блога, а другой - для преобразования списка Wrapper в блог → Список блогов. Тогда вы бы:
- Конвертировать список блога в список обложек блога.
- Добавьте список оберток вашего блога в набор хешей.
- Получите список скомпонованных оберток блога из набора хешей.
- Преобразование списка оберток в блог в список блогов
Код для Blog Wrapper будет примерно таким:
import java.util.ArrayList;
import java.util.List;
public class BlogWrapper {
public static List<Blog> unwrappedList(List<BlogWrapper> blogWrapperList) {
if (blogWrapperList == null)
return new ArrayList<Blog>(0);
List<Blog> blogList = new ArrayList<Blog>(blogWrapperList.size());
for (BlogWrapper bW : blogWrapperList) {
blogList.add(bW.getBlog());
}
return blogList;
}
public static List<BlogWrapper> wrappedList(List<Blog> blogList) {
if (blogList == null)
return new ArrayList<BlogWrapper>(0);
List<BlogWrapper> blogWrapperList = new ArrayList<BlogWrapper>(blogList
.size());
for (Blog b : blogList) {
blogWrapperList.add(new BlogWrapper(b));
}
return blogWrapperList;
}
private Blog blog = null;
public BlogWrapper() {
super();
}
public BlogWrapper(Blog aBlog) {
super();
setBlog(aBlog);
}
public boolean equals(Object other) {
// Your equality logic here
return super.equals(other);
}
public Blog getBlog() {
return blog;
}
public int hashCode() {
// Your hashcode logic here
return super.hashCode();
}
public void setBlog(Blog blog) {
this.blog = blog;
}
}
И вы можете использовать его так:
List<BlogWrapper> myBlogWrappers = BlogWrapper.wrappedList(your blog list here);
Set<BlogWrapper> noDupWrapSet = new HashSet<BlogWrapper>(myBlogWrappers);
List<BlogWrapper> noDupWrapList = new ArrayList<BlogWrapper>(noDupSet);
List<Blog> noDupList = BlogWrapper.unwrappedList(noDupWrapList);
Совершенно очевидно, что вы можете сделать вышеописанный код более эффективным, в частности, сделав методы обтекания и разворота на блоке Wrapper вместо коллекций вместо коллекций.
Альтернативным способом обертывания класса Blog будет использование библиотеки манипуляции с байтовым кодом, например BCEL, чтобы фактически изменить значения equals и hashcode метод для блога. Но, конечно, это может иметь непреднамеренные последствия для остальной части вашего кода, если они требуют первоначального поведения equals/hashcode.
Ответ 16
Самый простой и эффективный способ - позволить eclipse генерировать и переопределять метод equals и hashcode. Просто выберите атрибуты для проверки дубликатов при появлении запроса, и вы должны быть настроены.
Также, как только список будет готов, поместите его в Set, и у вас есть дубликаты.
Ответ 17
Рекомендуется переопределить equals()
и hashCode()
для работы с коллекциями на основе хешей, включая HashMap
, HashSet
и Hashtable
Таким образом, вы можете легко удалить дубликаты, инициировав объект HashSet
со списком блогов.
List<Blog> blogList = getBlogList();
Set<Blog> noDuplication = new HashSet<Blog>(blogList);
Но благодаря Java 8, который имеет очень чистую версию для этого, как вы упоминали, вы не можете изменить код для добавления equals()
и hashCode()
Collection<Blog> uniqueBlogs = getUniqueBlogList(blogList);
private Collection<Blog> getUniqueBlogList(List<Blog> blogList) {
return blogList.stream()
.collect(Collectors.toMap(createUniqueKey(), Function.identity(), (blog1, blog2) -> blog1))
.values();
}
List<Blog> updatedBlogList = new ArrayList<>(uniqueBlogs);
Третий параметр Collectors.toMap()
- это функция слияния (функциональный интерфейс), используемая для разрешения конфликтов между значениями, связанными с одним и тем же ключом.