为什么当我的条件为true时我的if循环会被跳过?

时间:2019-10-13 10:29:52

标签: java

我正在编写一个小程序,在其中添加一个String的ArrayList,并且有一个方法可以删除每个以S结尾的String,它对于4个元素都可以正常工作,但是似乎跳过了一个。

最小的可复制示例:

import java.util.ArrayList;
public class sList {
    public static void main(String [] args) {

    ArrayList<String> sList=new ArrayList<String>();

    sList.add("leaf");
    sList.add("leaves");
    sList.add("box");
    sList.add("boxes");
    sList.add("phones");
    sList.add("phone");
    method m=new methods(); 
System.out.println(m.removePlurals(sList));

}
}

\\ that is my main method 

import java.util.ArrayList;

public class method {

ArrayList<String> removePlurals(ArrayList<String> s) {

        for (int i = 0; i < s.size(); i++) {
            char c = s.get(i).charAt(s.get(i).length() - 1);
            if (c == 's') {
                s.remove(i);
            }

        }
        return s;
    }

}

我得到的输出是:[叶子,盒子,电话,电话],所以它跳过了“电话” 有帮助吗?

4 个答案:

答案 0 :(得分:0)

这是因为当您删除列表中的一项时,列表大小小于开始时的大小,并且当您的计数器增加时,它指向下一个元素(跳过一个元素)。
您可以解决此问题,只需以另一种方式循环列表,如下所示:

 ArrayList<String> removePlurals(ArrayList<String> s) {

        for (int i = s.size()-1; i >= 0; i--) {
            char c = s.get(i).charAt(s.get(i).length() - 1);
            if (c == 's') {
                s.remove(i);
            }

        }
        return s;
    }

}

答案 1 :(得分:0)

考虑一下从列表中删除“框”后会发生什么。 “电话”的索引减少1 。如果它最初是列表中的第n个项目,那么现在它是列表中的第(n-1)个项目,不是吗?因此,现在“电话”在列表中的位置与“盒子”最初的位置相同。另一方面,i增加1。

现在您应该看到问题了,要检查“电话”,i应该保持不变,因为在删除“盒子”之后,“电话”的索引减少了1。

解决方案,只需从列表的末尾开始循环,就不会出现此问题:

for (int i = s.size() - 1; i >= 0; i--) {
    char c = s.get(i).charAt(s.get(i).length() - 1);
    if (c == 's') {
        s.remove(i);
    }

}

答案 2 :(得分:0)

之所以会这样,是因为您要迭代的列表在同一循环中正在更新。因此,当您删除一个元素是上一次迭代时,它的索引将被更新,并且元素根本不会被循环拾取。所以解决方案应该像这样:

1)创建新列表并返回:

ArrayList<String> removePlurals(final ArrayList<String> s) {

    final ArrayList<String> updatedList = new ArrayList<String>();

    for (int i = 0; i < s.size(); i++) {
      final char c = s.get(i).charAt(s.get(i).length() - 1);
      if (c != 's') {
        updatedList.add(s.get(i));
      }

    }
    return updatedList;
  }

2)使用迭代器:

ArrayList<String> removePlurals(final ArrayList<String> s) {

        final Iterator<String> itr = s.iterator();
        while (itr.hasNext()) {
        final String x = itr.next();
        if (x.charAt(x.length() - 1) == 's') {
           itr.remove();
        }
        }
        return s;  
      }

答案 3 :(得分:0)

除了问题中报告的问题之外,您还存在一个问题:一次从ArrayList的中间删除一个元素确实效率不高,因为您不断在{{1 }},然后在列表的末尾加上一个位置-然后您对大多数对象再次执行此操作。

性能不应该是您首先要考虑的问题-慢,正确的代码总是比快速,错误的代码更好-但是您应该避免某些事情需要避免的问题:在这种情况下,(i+1)的中间>重复删除。

更好的解决方案是不不断移动元素,而只是移动一次。

与向后迭代列表的解决方案不同,可以向前迭代来完成,这感觉更自然(至少对我而言)。

ArrayList

这会将要保留的每个元素移动到新位置。剩下的就是砍掉列表的末尾:

int target = 0;
for (int i = 0; i < s.size(); ++i) {
  if (!s.get(i).endsWith("s")) {
    s.set(target, s.get(i));
    target++;
  }
}

或者,只需一次操作:

while (s.size() > target) s.remove(s.size()-1);

一起:

s.subList(target, s.size()).clear();

请注意,这实际上是int target = 0; for (int i = 0; i < s.size(); ++i) { if (!s.get(i).endsWith("s")) { s.set(target, s.get(i)); target++; } } s.subList(target, s.size()).clear(); 完成的操作:

ArrayList.removeIf

({s.removeIf(e -> e.endsWith("s")); 多做一点 ,因为它以故障原子的方式进行删除,也就是说,如果removeIf由于某种原因而失败, (例如null元素),列表保持不变,而不是部分更新。