【问题标题】:Trouble removing duplicates in a list无法删除列表中的重复项
【发布时间】:2017-11-14 22:44:00
【问题描述】:

我最近一直在处理具有日期(实际上是字符串)和 ID 的对象列表。日期以这种方式使用字符串存储:“yyyy-mm-dd”。 所以我有一个很长的列表(大约 80 000 个元素),看起来像这样:

[{id:"15655454", date:"2014-11-15"}, {id:"15655454", date:"2014-11-15"}, {id:"15655454", date:"2014-11-15"}, {id:"15655454", date:"2014-10-17"}, {id:"15655454", date:"2014-10-17"}, {id:"15655454", date:"2014-10-17"}, {id:"15655454", date:"2014-10-17"}, {id:"15655454", date:"2014-10-17"}, {id:"15655455", date:"2014-09-23"}, ...]

其实是一个java类,稍微复杂一点,但是全局的思路就在这里。 (实际上它确实不止两个字段,但我认为这并不重要)

如您所见,它是按 ID 排序的。

我想做的是: - 如果一个 ID 有多个日期:仅保留最新日期。 - 如果一个 ID 有多个相同的最新日期:保留所有日期。

所以这是我迄今为止尝试过的:

List<Element> results = new ArrayList<Element>();
results.initiateList();

int count = 0;

while(count < results.size()-1) {
    if (results.get(count).getID().equals(results.get(count+1).getID())) {
            String[] dateI = results.get(count).getDate().split("-");
            String[] dateJ = results.get(count+1).getDate().split("-");
            int yearI = Integer.parseInt(dateI[0]);
            int yearJ = Integer.parseInt(dateJ[0]);
            int monthI = Integer.parseInt(dateI[1]);
            int monthJ = Integer.parseInt(dateJ[1]);
            int dayI = Integer.parseInt(dateI[2]);
            int dayJ = Integer.parseInt(dateJ[2]);

            if (results.get(count).getDate()== null) {
                results.remove(count);
                if (count != 0) {
                    count--;
                }
            } else if (results.get(count+1).getDate()== null) {
                results.remove(count+1);
            } else if ( yearI > yearJ ) {
                results.remove(count+1);
            } else if ( yearI < yearJ ) {
                results.remove(count);
                if (count != 0) {
                    count--;
                }
            } else if ( yearI == yearJ && monthI > monthJ ) {
                results.remove(count+1);
            } else if ( yearI == yearJ && monthI < monthJ ) {
                results.remove(count);
                if (count != 0) {
                    count--;
                }
            } else if ( yearI == yearJ && monthI == monthJ && dayI > dayJ ) {
                results.remove(count+1);
            } else if ( yearI == yearJ && monthI == monthJ && monthI < monthJ ) {
                results.remove(count);
                if (count != 0) {
                    count--;
                }
            } else if ( yearI == yearJ && monthI == monthJ && dayI == dayJ ) {
                count++;
            }
        } else {
            count++;
        }


    }

但这不起作用,我不明白为什么。我觉得我已经考虑了所有可能的情况,但它仍然没有删除所有重复项。问题是我仍然有一些重复,例如,列表仍然会包含一个 ID 的多个不同日期。

initialList()、getDate() 和 getID() 方法都得到了正确的实现,因为我在很多其他不同的地方都使用过它们,而且它们在那里工作得很好。 InitiateList() 将所有元素放在列表中,如果我尝试在控制台上显示它们,它可以正常工作。

我看不出 while 循环有什么问题,但我想我错过了一些东西(它可能很基本......)

【问题讨论】:

  • 试试调试器。并一步一步看。创建一个代码失败的小示例(并将其添加到您的问题中)。
  • 考虑使用Map 来存储您的id-date 对。如果id 存在,则检查date 是否是最新的——是的,存储它,否则忽略。此外,将您的日期解析为 LocalDate 之类的内容,让您的生活更轻松。
  • IOC:概念的实现。首先尝试在一个小用例上实现逻辑,验证该逻辑,然后再转到实际用例。
  • 为我们提供重现问题的工作代码。这是伪代码,可以通过这一行在 bat 右侧看到:results.initiateList(); 还有几个问题,例如:您执行==null 检查如果它为空,它会在哪里抛出 NPE。
  • 按 id 然后按日期排序(意思是,如果 id 相等,则比较日期)。然后解析您的列表并保留您找到的每个第一个新 id。

标签: java algorithm list duplicates


【解决方案1】:

我认为尝试与相邻元素进行比较是错误的,因为它们没有必要都具有相同的 id。也许您应该尝试不同的方法,在类似这样的收集器中使用 groupBy

 public List<Element> removeDuplicates(List<Element> theList) {
    // Getting a map where key is an id of element and the list is all the elements with the same id
    Map<String, List<Element>> theData = theList.stream().collect(Collectors.groupingBy(elem -> elem.getId()));

    List<Element> result = new ArrayList<>();
    //Now we go over the map and from each list we take the element with newes date
    theData.forEach((id, elementsList) -> {
        Element elementWithNewstDate = getNewest(elementsList);
        result.add(elementWithNewstDate);
    });

    return result;
}

private Element getNewest(List<Element> elementsList) {
    // Sorting by newest date
    elementsList.sort(Comparator.comparing(element -> getDateFromElement(element)));
    //Taking the first one becouse it supose to be the newst if i am wrong take the last element
    return elementsList.get(0);
}

private Date getDateFromElement(Element element) {
    Date result = null;
    try {
        result = new SimpleDateFormat("yyyy-MM-dd").parse(element.getDate());
    } catch (ParseException e) {
        e.printStackTrace();
    }
    return result;
}

请注意,它不是 100% 正确,但作为示例应该会有所帮助

【讨论】:

  • 您的第一个短语是错误的 - 列表按 ID 排序
  • 谢谢。它现在工作正常。我不得不修改一点 getNewest() 以便它可以返回一个列表(因为我可以有几个日期等于最新的日期,在这种情况下我将它们全部保留)但这很容易从你的工作中获得。我不擅长比较器,这是如何使用它的一个很好的例子。
  • 乐于助人:)
【解决方案2】:

您正在进行不必要的比较。您可以使用下面的 sn-p 将字符串转换为日期。

    String sDate1="2014-11-15";  
    Date date1=new SimpleDateFormat("yyyy-MM-dd").parse(sDate1);
    System.out.println("Date : "+date1);
    System.out.println("Date value :"+date1.getTime());

您可以比较相应日期 getTime() 的输出来决定更高/更低的日期。您可以减少代码并进行调试。

【讨论】:

  • 其实字符串如果格式化为'yyyy-MM-dd'可以直接比较
猜你喜欢
  • 2018-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-19
  • 2011-10-04
  • 2014-09-30
  • 2014-06-12
相关资源
最近更新 更多