关于java:删除重复的ArrayList自定义对象

Remove duplicates ArrayList custom object

本问题已经有最佳答案,请猛点这里访问。

我有一个arraylist,它包含类Event的元素。事件有两个属性,NameTimestamp。列表现在显示所有事件。我想删除具有相同名称但时间戳不同的重复项,并将它们放在另一个列表中。这样,用户可以单击具有该名称的事件,然后选择日期。

对于我的应用程序中的其他一些功能,我已经重写了equals函数(用于比较名称和时间戳)。

我怎么解决这个问题?


如果您已经有了自己的equals方法,则不能使用Hash集合。必须手动检查它是否实现了嵌套循环:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);
}


我认为您使用的数据结构错误。您希望使用Map的实现,并将String(名称)映射到Set(唯一事件)。

下面是我们测试它的方法:

  • 创建一些事件。
  • 创建Map。这将允许我们将名称映射到唯一的事件。
  • 填充映射。
  • 因此,首先,我们创建一组要测试的事件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
        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)));
            }
        };

    现在,我们在一个名称和它对应的所有唯一事件之间创建一个映射:

    1
        Map<String, Set<Event>> eventsByName = new HashMap<String, Set<Event>>();

    现在,我们用每个名称的唯一事件填充映射:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
        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);

        }

    检查我们得到了什么:

    1
        System.out.println(eventsByName);

    输出:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    {
        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的键:

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

    输出:

    1
    [SecondCategory, FirstCategory]

    提示2:

    如果这不是您所期望的,并且您想要一个不同的唯一性定义,那么您可以实现一个Comparator,并将其与TreeSet一起使用,而不是使用不能接受自定义ComparatorHashSet

    所以如果你有一门课:

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

    这是填充映射时需要完成的全部工作:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
        // 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);

        }

    祝你好运。


    您应该在事件类中重写您的equals()hashCode()方法,并将所有对象添加到Set而不是List中。如果您正确地覆盖了equals()hashCode()Set将不允许复制对象。