您的位置:首页 > 其它

反向思维的妙处---解决正向循环删除元素的问题

2012-09-15 16:01 239 查看
循环是我们代码中最常使用的结果,在遍历的基础上进行其他操作,比如删除。如果是使用List容器,那么就更加简单了,因为List封装了许多实用的方法,拿删除来说,就有remove()和removeAll()。拿来主义固然是好事,但是不注意拿来的东西到底怎么用,就会出问题。鲁迅的文章早已经指出这点,所以,我们也要对我们”拿来“的东西研究一下。

remove()首当其冲就给了我一个”下马威"。remove()这个方法最大的毛病就是改变List的结构,它会将List中想要移除的元素后面的所有元素向前移动一位。我们可以通过下面的代码来看看这个“可怕”的副作用:

代码如下:

public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("java");
list.add("C++");
list.add("java");
list.add("java");
list.add("java");
list.add("C");
System.out.println("看看原先要删除的元素所在的位置:");
for (int i = 0, len = list.size(); i < len; i++) {
if (list.get(i).equals("java")) {
System.out.println("location:" + i + ":" + list.get(i));
}

}
System.out.println("看看是哪些位置的元素被删除:");
for (int i = 0, len = list.size(); i < len; i++) {
if (list.get(i).equals("java")) {
System.out.println("delete:" + i + ":" + list.get(i));
list.remove(i);
}
}
System.out.println("看看现在那些剩下的元素的位置:");
for (int i = 0, len = list.size(); i < len; i++) {
System.out.println(i + ":" + list.get(i));
}
}


输入结果如下:

看看原先要删除的元素所在的位置:
location:0:java
location:2:java
location:3:java
location:4:java
看看是哪些位置的元素被删除:
delete:0:java
delete:1:java
delete:2:java
看看现在那些剩下的元素的位置:
0:C++
1:java
2:C

看到没有?有一个元素是没有被删除的,而且删除的位置与它们原来的位置也不一样啊!我们把这个过程模拟出来:java(0),C++(1),java(2),java(3),java(4),C(5) --->C++(0),java(1),java(2),java(3),C(4)--->C++(0),java(1),java(2),C(3)--->C++(0),java(1),C(2)。这里面要删除的是两个java(1)!!但是remove()中的每个索引参数只会被删除一次,所以,倒数第二个java是不会被删除的!!

如果大家还不信,我还有一个验证代码可以做证据:

public static void main(String[] args) {
List<RatingBook> list = new ArrayList<RatingBook>();
RatingBook book1 = new RatingBook();
book1.setBook("java");
book1.setRating(0);
RatingBook book2 = new RatingBook();
book2.setBook("C++");
book2.setRating(1);
RatingBook book3 = new RatingBook();
book3.setBook("java");
book3.setRating(2);
RatingBook book4 = new RatingBook();
book4.setBook("java");
book4.setRating(3);
RatingBook book5 = new RatingBook();
book5.setBook("java");
book5.setRating(4);
RatingBook book6 = new RatingBook();
book6.setBook("C");
book6.setRating(5);
list.add(book1);
list.add(book2);
list.add(book3);
list.add(book4);
list.add(book5);
list.add(book6);
for (int i = 0, len = list.size(); i < len; i++) {
if (((list.get(i).getBook()).equals("java"))) {
list.remove(i);
}
}
for (int i = 0, len = list.size(); i < len; i++) {
System.out.println(i + ":" + list.get(i).getBook() + " "
+ list.get(i).getRating());
}
}


RatingBook的代码如:

public class RatingBook {
private int rating;
private String book;

public void setRating(int rating) {
this.rating = rating;
}

public void setBook(String book) {
this.book = book;
}

public String getBook() {
return book;
}

public int getRating() {
return rating;
}

}


结果如下:

0:C++ 1
1:java 3
2:C 5

留下来的是倒数第二个java啊!!!

所以,在循环中使用remove()来删除重复的元素必须打起十二分精神。这个问题的解决也不难,犯不着从此不用remove(),它依然是非常好用的,只是我们会不会用而已。我这里有一个小技巧,无论序列中是否有重复的元素都可以使用,就是反向遍历List然后删除元素。道理很简单,既然remove()是将目标元素后面的元素向前移动,那么,只要目标元素后面没有元素不就可以了?反向就是利用了这点,只要将我上面的代码改成这样:

for(int i = list.size() - 1; i >= 0; i++){
...
}


这样子处理List是比较好的,因为它可以避免我们使用remove()这些会改变结构的方法带来的影响,也可以一定程度上提高我们的循环效率。循环是程序中耗费最大的一块,关于它的优化的话题一直都是重点,层出不穷,这里就是一点。我们很少注意到,int i = 0; i < list.size()是要付出很大的开销的,因为我们每次判断循环终点条件的时候都会执行一次list.size()的计算,如果list很大的话,开销就很大了。反向的话,list.size()是放在前面的,就不用每次都计算了。

如果只是想要完全删除重复的元素,我们可以使用removeAll()。它里面的参数类型是Collection<?>,是一个容器,至于为什么是容器,接下来我会讲的,就是有关于removeAll()的原理。

先上代码:

public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("java");
list.add("C++");
list.add("java");
list.add("java");
list.add("java");
list.add("C");
System.out.println("看看原先要删除的元素所在的位置:");
for (int i = 0, len = list.size(); i < len; i++) {
if (list.get(i).equals("java")) {
System.out.println("location:" + i + ":" + list.get(i));
}

}
List<String> sublist = new ArrayList<String>();
sublist.add("java");
list.removeAll(sublist);
System.out.println("看看现在那些剩下的元素的位置:");
for (int i = 0, len = list.size(); i < len; i++) {
System.out.println(i + ":" + list.get(i));
}
}


结果如:

看看原先要删除的元素所在的位置:
location:0:java
location:2:java
location:3:java
location:4:java
看看现在那些剩下的元素的位置:
0:C++
1:C

之所以不像上面那样显示要删除的元素的位置,是因为这个过程是将整个list都遍历一次,然后将与sublist相同的元素找出来并且删除掉,而且因为是容器,你可以添加更多的元素,反正只要list中有sublist的元素,就会全部删掉。这样就会避免上面的错误,但是,必须注意,最好是原先的list是怎样的形式,作为参数的容器也是同样的形式,以免出现不可匹配的问题。

至于如何在循环中删除重复的元素,欢迎看一下我的文章:/article/4889749.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: