【问题标题】:Java, removing elements from an ArrayListJava,从 ArrayList 中删除元素
【发布时间】:2016-07-23 11:15:38
【问题描述】:

我遇到了这个项目的问题。基本前提是用户输入一个短语,它应该找到任何重复的单词以及有多少。

我的问题是多次输入一个单词时,例如... 你好你好你好你好

输出结果是;

"There are 2 duplicates of the word "hello" in the phrase you entered." 
"There are 1 duplicates of the word "hello" in the phrase you entered." 

这似乎只发生在这样的情况下。如果我输入一个随机短语,其中包含多个单词,它会显示正确答案。我认为这个问题与删除重复的单词以及它在短语中迭代的次数有关,但我就是无法理解它。我已经在各处添加了打印行,并改变了它以各种方式迭代的时间,我在 Java Visualizer 中通过它,但仍然找不到确切的问题。非常感谢任何帮助!

这是我的在线 Java 课程的作业,但仅用于学习/练习,不适合我的专业。尽管只是提供帮助,但我不是在寻找答案。

public class DuplicateWords {

public static void main(String[] args) {

    List<String> inputList = new ArrayList<String>();
    List<String> finalList = new ArrayList<String>();

    int duplicateCounter;
    String duplicateStr = "";
    Scanner scan = new Scanner(System.in);

    System.out.println("Enter a sentence to determine duplicate words entered: ");
    String inputValue = scan.nextLine();
    inputValue = inputValue.toLowerCase();
    inputList = Arrays.asList(inputValue.split("\\s+"));
    finalList.addAll(inputList);


    for(int i = 0; i < inputList.size(); i++) {
        duplicateCounter = 0;
        for(int j = i + 1; j < finalList.size(); j++) {
            if(finalList.get(i).equalsIgnoreCase(finalList.get(j))
                    && !finalList.get(i).equals("!") && !finalList.get(i).equals(".")
                    && !finalList.get(i).equals(":") && !finalList.get(i).equals(";")
                    && !finalList.get(i).equals(",") && !finalList.get(i).equals("\"")
                    && !finalList.get(i).equals("?")) {
                duplicateCounter++;
                duplicateStr = finalList.get(i).toUpperCase();
            }
            if(finalList.get(i).equalsIgnoreCase(finalList.get(j))) {
                finalList.remove(j);
            }

        }
        if(duplicateCounter > 0) {
            System.out.printf("There are %s duplicates of the word \"%s\" in the phrase you entered.", duplicateCounter, duplicateStr);
            System.out.println();
        }
    }       
}
}

根据一些建议,我编辑了我的代码,但我不确定我的方向是否正确

String previous = "";

    for(Iterator<String> i = inputList.iterator(); i.hasNext();) {
        String current = i.next();
        duplicateCounter = 0;
        for(int j =  + 1; j < finalList.size(); j++) {
            if(current.equalsIgnoreCase(finalList.get(j))
                    && !current.equals("!") && !current.equals(".")
                    && !current.equals(":") && !current.equals(";")
                    && !current.equals(",") && !current.equals("\"")
                    && !current.equals("?")) {
                duplicateCounter++;
                duplicateStr = current.toUpperCase();
            }
            if(current.equals(previous)) {
                i.remove();
            }

        }
        if(duplicateCounter > 0) {
            System.out.printf("There are %s duplicates of the word \"%s\" in the phrase you entered.", duplicateCounter, duplicateStr);
            System.out.println();
        }
    }

【问题讨论】:

  • 您正在迭代一个 ArrayList,同时从中删除项目。这会导致意外行为。一个安全的使用方法是Iterator.remove(),见stackoverflow.com/a/223929/4190526
  • 如果你想在删除数组的同时迭代一个数组,我建议你从数组的末尾开始迭代到顶部。

标签: java list arraylist iterator


【解决方案1】:

您的代码的问题是,当您删除一个项目时,您仍然会增加索引,因此您会跳过下一个项目。在缩写形式中,您的代码是:

 for (int j = i + 1; j < finalList.size(); j++) {
     String next = finalList.get(i);
     if (some test on next)
         finalList.remove(next);
 }

在调用 remove 之后,“下一个”项目将位于相同的索引处,因为像这样直接删除一个项目会导致右侧的所有项目被随机移动 1 个位置以填补空白。要修复,您应该在删除后添加此行:

 i--;

这可以解决您的问题,但是,有一种更简洁的方法:

 String previous = "";
 for (Iterator<String> i = inputList.iterator(); i.hasNext();) {
    String current = i.next();
    if (current.equals(previous)) {
        i.remove(); // removes current item
    }
    previous = current;
 }

inputList 现在已删除所有相邻的重复项。


要删除 所有 个重复项:

List<String> finalList = inputList.stream().distinct().collect(Collectors.toList());

如果您喜欢疼痛,请“手动”进行:

 Set<String> duplicates = new HashSet<>(); // sets are unique
 for (Iterator<String> i = inputList.iterator(); i.hasNext();)
    if (!duplicates.add(i.next())) // add returns true if the set changed
        i.remove(); // removes current item

【讨论】:

  • 所以,当像上面那样使用 Iterator 时,您将 i 分配给 next,然后我将使用 next 代替 i,对吗?我是否也会对内部 for 循环使用迭代器方法?
  • @sjud9227 我已经编辑了“正确的”代码,使其更清晰并向您展示整个内容。主要区别在于iterator.remove() 不会改变iterator.next() 返回的内容。
  • 我还是有点糊涂,抱歉,我在电脑上呆了 7 个小时,我的大脑被炸了。所以你是说我不需要内部 (j) for 循环?
  • @sjud9227 是的 - 假设您只想删除 adjacent 重复项,则不需要内部循环。所以“hello hello hello world hello”会变成“hello world hello”。
  • 我想在第一次迭代计数后删除所有重复项,这样它就不会一遍又一遍地重复答案。例子。 “你好,你好,你好,世界你好”会得到“‘你好’这个词有 3 个重复项”和“‘世界’这个词有 1 个重复项”的答案,我正在删除重复项,所以它不会继续每次通过计数重复。
【解决方案2】:

我会首先为每个单词填充一个Map&lt;String, Integer&gt;;每次遇到单词时增加Integer。类似的东西

String inputValue = scan.nextLine().toLowerCase();
String[] words = inputValue.split("\\s+");
Map<String, Integer> countMap = new HashMap<>();
for (String word : words) {
    Integer current = countMap.get(word);
    int v = (current == null) ? 1 : current + 1;
    countMap.put(word, v);
}

然后您可以迭代MapentrySet 并显示计数大于1 的每个key (word)。类似的,

String msgFormat = "There are %d duplicates of the word \"%s\" in "
        + "the phrase you entered.%n";
for (Map.Entry<String, Integer> entry : countMap.entrySet()) {
    if (entry.getValue() > 1) {
        System.out.printf(msgFormat, entry.getValue(), entry.getKey());
    }
}

【讨论】:

  • 打印每个副本的结果我不会有同样的问题。意思是“你好”会打印出 4 个重复项,然后下一次通过它会说有 3 个重复项“你好”....这就是为什么我在遍历列表时开始删除重复项。
  • @sjud9227 不是。因为每个单词都是一个键,因此在Map唯一
  • 哦,好的。我对地图不是很了解,今晚我必须提交这个,我今天可能会跳过地图,然后再回来练习。
【解决方案3】:

在将inputList 添加到finalList 之前,请从inputList 中删除所有重复项。

【讨论】:

  • 我无法从 inputList 中删除任何内容,因为我使用 Arrays.asList 来拆分用户输入,并且我猜此时 List 是不可修改的。
猜你喜欢
  • 2013-02-24
  • 1970-01-01
  • 1970-01-01
  • 2021-01-25
  • 2023-02-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多