我正在编写一个小程序,在其中添加一个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;
}
}
我得到的输出是:[叶子,盒子,电话,电话],所以它跳过了“电话” 有帮助吗?
答案 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元素),列表保持不变,而不是部分更新。