移除自定义对象的ArrayList中的重复项

9
我有一个 ArrayList,其中包含一个名为“Event”的类的元素。 Event 这个类有两个属性: Name 和 Timestamp。 这个列表现在显示所有事件。 我想要删除相同名称但不同时间戳的重复项,并将它们放入另一个列表中。这样用户就可以点击具有该名称的事件,然后选择日期。
我已经覆盖了 equals 函数(比较名称和时间戳)以用于应用程序的某些其他功能。
我该如何解决这个问题?

你尝试过使用 HashSet 而不是 ArrayList 吗? - Abubakkar
你是否有同名和相同时间戳的情况,而你又想将它们保存在列表中? - Keval
你必须保留哪些事件?首先找到的吗? - Jordi Castilla
请点击此链接这里。这展示了一个列表的替代方案。如果可能,您可以采用这种方式。否则请评论。 - XylemRaj
1
为什么这个被标记为重复?该重复链接没有回答问题,只是其中的一个小部分,并且在另一部分上做错了。我引用:“我想删除具有相同名称但时间戳不同的重复项,然后将它们放入另一个列表中。”似乎过滤甚至不是实际目标:“这样用户就可以单击一个具有该名称的事件,然后选择日期。” - Reut Sharabani
3个回答

31
如果您已经拥有自己的等值方法,您无法使用Hash集合。您必须手动检查并实现嵌套循环:

如果您已经有了自己的等于方法,则无法使用Hash集合。您必须手动检查并实现嵌套循环:

List<Event> allEvents = // fill with your events.
List<Event> noRepeat = new ArrayList<Event>();

for (Event event : allEvents) {
    boolean isFound = false;
    // check if the event name exists in noRepeat
    for (Event e : noRepeat) {
        if (e.getName().equals(event.getName()) || (e.equals(event))) {
            isFound = true;        
            break;
        }
    }
    if (!isFound) noRepeat.add(event);
}

我认为这不是完整的解决方案。我想要删除具有相同名称但时间戳不同的重复项,并将它们放入另一个列表中。您还必须检查时间戳是否相等。 - Ziker
他有一个equals方法来检查两个参数,他需要从列表中删除同名事件:OP说:“我想删除具有相同名称但不同时间戳的重复项”,这就是我的方法所做的... - Jordi Castilla
1
是的,但由于ArrayList可以包含重复项,所以条件应该是if (e.getName().equals(event.getName() || e.equals(event)),以删除具有相同名称和时间戳的不同对象。 - Ziker
实际上它违反了自然逻辑,但是这是真的... 棒极了! - Jordi Castilla
@JordiCastilla 一个不错的方法是使用自定义的Comparator<Event>(用于比较),并使用TreeSet<Event>(使用该比较器创建集合)。这也可以强制清晰度,因为使用正确的接口传达了想法。请参见我的答案的最后一部分。 - Reut Sharabani
2
非常感谢,这解决了我的问题!! - Nicolas Zawada

2

我认为您使用的数据结构不正确。您需要使用Map的实现,并将String(名称)映射到Set<Event>(唯一事件)。

以下是我们如何测试它:

  1. 创建一些事件。
  2. 创建Map<String,Set<Event>。这将允许我们将名称映射到唯一事件。
  3. 填充映射。

因此,首先我们创建了一个事件集合进行测试:

    Collection<Event> events = new ArrayList<Event>() {
        /**
         * 
         */
        private static final long serialVersionUID = 1L;

        {
            add(new Event("FirstCategory", new Timestamp(0)));
            add(new Event("FirstCategory", new Timestamp(0)));
            add(new Event("FirstCategory", new Timestamp(1)));
            add(new Event("SecondCategory", new Timestamp(2)));
        }
    };

现在我们要创建一个名称和其对应的所有唯一事件之间的映射:
    Map<String, Set<Event>> eventsByName = new HashMap<String, Set<Event>>();

现在,我们为每个名称填充具有唯一事件的映射:
    for (Event e : events) {
        if (!eventsByName.containsKey(e.getName())) {
            // create new set by name
            eventsByName.put(e.getName(), new HashSet<Event>());

        }
        // add event to existing Set.
        // duplicates will be dropped since it's a `Set`
        eventsByName.get(e.getName()).add(e);

    }

检查我们得到了什么:

    System.out.println(eventsByName);

输出:

{
    SecondCategory=[
        Event [name=SecondCategory, timestamp=1970-01-01 02:00:00.002]
    ],
    FirstCategory=[
        Event [name=FirstCategory, timestamp=1970-01-01 02:00:00.0],
        Event [name=FirstCategory, timestamp=1970-01-01 02:00:00.001]
    ]
}

提示1:

要获取名称列表,您只需要查看Map的键,这些键实际上也是一个Set

System.out.println(eventsByName.keySet());

输出:

[SecondCategory, FirstCategory]

提示2:

如果这不是你所期望的,而你想要不同的唯一性定义,你可以实现一个Comparator<Event>,并将其与TreeSet<Event>一起使用,而不是使用无法接受自定义ComparatorHashSet<Event>

因此,如果你有一个类:

class EventByRandomDefinitionComparator implements Comparator<Event>{
    // implementation ...
}

当填写映射时,只需要完成以下步骤:

    // create different comparison mechanism
    Comparator<Event> comparator = new EventByRandomDefinitionComparator();

    for (Event e : events) {
        if (!eventsByName.containsKey(e.getName())) {
            // create new set by name
            // changed Set implementation to use new comparator
            eventsByName.put(e.getName(), new TreeSet<Event>(comparator)));
        }
        // add event to existing Set.
        // duplicates will be dropped since it's a `Set`
        eventsByName.get(e.getName()).add(e);

    }

祝你好运。


谢谢您的全面回答。如果一开始就使用了地图,那么这将会更容易。另一个答案解决了我的问题,但我会记住地图以备将来之需。现在我将使用地图将同一事件链接到多个时间戳。 - Nicolas Zawada
无论是什么阻止你现在使用正确的解决方案,如果你添加更多基于错误实现的代码,这些问题只会变得更糟。使用正确的工具可以使代码更易于维护、测试和理解。如果这个答案缺少了你想要实现的东西,请将其添加到你的问题中,我可以尝试扩展答案以包含它。 - Reut Sharabani

-1
你应该在你的事件类中重写equals()hashCode()方法,并将所有对象添加到一个Set而不是List中。如果你正确地重写了equals()hashCode(),那么Set将不允许重复的对象存在。

OP必须将“equals”用于其他目的...请仔细阅读。 - Jordi Castilla

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接