集合--(List、Set、Map)遍历、删除、比较元素时的小陷阱
6,Map集合遍历的4中方法?
5,List遍历时如何remove元素
4、漏网之鱼-for循环递增下标方式遍历集合,并删除元素
如果你用for循环递增下标方式遍历集合,在遍历过程中删除元素,你可能会遗漏了某些元素。
3、ConcurrentModificationException异常-----Iterator遍历集合过程中用其他手段(或其他线程)操作元素
2、Map集合操作陷阱;
1、Set集合操作陷阱---一个不包含重复元素的 collection。更确切地讲,set 不包含满足 e1.equals(e2) 的元素对 e1 和 e2
=====================
6,Map集合遍历的4中方法?
在Java中如何遍历Map对象
在java中遍历Map有不少的方法。我们看一下最常用的方法及其优缺点。
既然java中的所有map都实现了Map接口,以下方法适用于任何map实现(HashMap, TreeMap, LinkedHashMap, Hashtable, 等等)
方法一 在for-each循环中使用entries来遍历
这是最常见的并且在大多数情况下也是最可取的遍历方式。在键值都需要时使用。
- Map<Integer, Integer> map = new HashMap<Integer, Integer>();
- for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
- System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
- }
注意:for-each循环在Java5中被引入所以该方法只能应用于java 5或更高的版本中。如果你遍历的是一个空的map对象,for-each循环将抛出NullPointerException,因此在遍历前你总是应该检查空引用。
方法二 在for-each循环中遍历keys或values。
如果只需要map中的键或者值,你可以通过keySet或values来实现遍历,而不是用entrySet。
- Map<Integer, Integer> map = new HashMap<Integer, Integer>();
- //遍历map中的键
- for (Integer key : map.keySet()) {
- System.out.println("Key = " + key);
- }
- //遍历map中的值
- for (Integer value : map.values()) {
- System.out.println("Value = " + value);
- }
该方法比entrySet遍历在性能上稍好(快了10%),而且代码更加干净。
方法三使用Iterator遍历
使用泛型:
- Map<Integer, Integer> map = new HashMap<Integer, Integer>();
- Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator();
- while (entries.hasNext()) {
- Map.Entry<Integer, Integer> entry = entries.next();
- System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
- }
不使用泛型:
- Map map = new HashMap();
- Iterator entries = map.entrySet().iterator();
- while (entries.hasNext()) {
- Map.Entry entry = (Map.Entry) entries.next();
- Integer key = (Integer)entry.getKey();
- Integer value = (Integer)entry.getValue();
- System.out.println("Key = " + key + ", Value = " + value);
- }
你也可以在keySet和values上应用同样的方法。
该种方式看起来冗余却有其优点所在。首先,在老版本java中这是惟一遍历map的方式。另一个好处是,你可以在遍历时调用iterator.remove()来删除entries,另两个方法则不能。根据javadoc的说明,如果在for-each遍历中尝试使用此方法,结果是不可预测的。
从性能方面看,该方法类同于for-each遍历(即方法二)的性能。
方法四、通过键找值遍历(效率低)
- Map<Integer, Integer> map = new HashMap<Integer, Integer>();
- for (Integer key : map.keySet()) {
- Integer value = map.get(key);
- System.out.println("Key = " + key + ", Value = " + value);
- }
作为方法一的替代,这个代码看上去更加干净;但实际上它相当慢且无效率。因为从键取值是耗时的操作(与方法一相比,在不同的Map实现中该方法慢了20%~200%)。如果你安装了FindBugs,它会做出检查并警告你关于哪些是低效率的遍历。所以尽量避免使用。
总结
如果仅需要键(keys)或值(values)使用方法二。如果你使用的语言版本低于java 5,或是打算在遍历时删除entries,必须使用方法三。否则使用方法一(键值都要)。
5,List遍历时如何remove元素?
public class RemoveElementDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("100012011");
list.add("10001201s1");
list.add("10001201s1");
//解决方案:
//1.i--操作
/*for(int i = 0;i < list.size();i++){
String b = list.get(i);
if(b.equals("502323232")){
list.remove(i);
i--;
}
}*/
//2.反向遍历
/*for(int i = list.size() - 1;i >= 0;i--){
String b = list.get(i);
if(b.equals("502323232")){
list.remove(i);
}
}*/
//解决方案:调用Iterator的remove()方法安全删除元素,避免异常
Iterator<String> iter = list.iterator();
while(iter.hasNext()){
String b = iter.next();
if(b.equals("100012011")){
iter.remove();
}
}
for(String b : list){
System.out.println(b);
}
}
}
------------
4、漏网之鱼-for循环递增下标方式遍历集合,并删除元素
如果你用for循环递增下标方式遍历集合,在遍历过程中删除元素,你可能会遗漏了某些元素。
public class ListTest_Unwork {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
System.out.println("Original list : " + list);
String temp = null;
for (int i = 0; i < list.size(); i++) {
temp = list.get(i);
System.out.println("Check for " + temp);
if ("3".equals(temp)) {
list.remove(temp);
}
}
System.out.println("Removed list : " + list);
}
}
日志打印:
Original list : [1, 2, 3, 4, 5]
Check for 1
Check for 2
Check for 3
Check for 5
Removed list : [1, 2, 4, 5]
如日志所见,其中值为4的元素并未经过判断,漏网之鱼。
解决方法为以下两个(但一般不建议我们在遍历中用不是遍历本身的函数删除元素,见关于“ConcurrentModificationException”的内容):
1、对于此情况,我一般都从后面开始遍历,以避免问题:
public class ListTest_Work {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
System.out.println("Original list : " + list);
String temp = null;
for (int i = list.size() - 1; i >= 0; i--) {
temp = list.get(i);
System.out.println("Check for " + temp);
if ("3".equals(temp)) {
list.remove(temp);
}
}
System.out.println("Removed list : " + list);
}
}
2、直接从新创建一个集合,重新摆放,但消耗内存,慎用:
public class ListTest_Work2 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
System.out.println("Original list : " + list);
System.out.println();
List<String> tempList = new ArrayList<String>();
for (String temp : list) {
System.out.println("Check for " + temp);
if (!"3".equals(temp)) {
tempList.add(temp);
}
}
System.out.println("Removed list : " + tempList);
}
}
3、ConcurrentModificationException异常-----Iterator遍历集合过程中用其他手段(或其他线程)操作元素;
ConcurrentModificationException是Java集合的一个快速报错(fail-fast)机制,防止多个线程同时修改同一个集合的元素。
在用Iterator遍历集合时,如果你用其他手段(非Iterator自身手段)操作集合元素,就会报ConcurrentModificationException。
用Iterator方式 或 --简写的for(Object o : list) {}方式,遍历集合,修改元素时会报异常:
延伸个小问题,为什么for(Object o : list) {}方式遍历集合,现象和Iterator方式一样,都会报错呢?
答:这是因为Java的糖语法,“for(Object o : list) {}方式”只是Java语言用“易用性糖衣”吸引你的手段,本质上,它也是Iterator。
2、Map集合操作陷阱;
方法1--- 在for-each循环中使用entries来遍历
这是最常见的并且在大多数情况下也是最可取的遍历方式。在键值都需要时使用。
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
System.out.println("Key="+entry.getKey()+",Value="+entry.getValue());
}
注意:for-each循环在Java 5中被引入所以该方法只能应用于java 5或更高的版本中。如果你遍历的是一个空的map对象,for-each循环将抛出NullPointerException,因此在遍历前你总是应该检查空引用。
方法2 ---在for-each循环中遍历keys或values。
如果只需要map中的键或者值,你可以通过keySet或values来实现遍历,而不是用entrySet。
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
//遍历map中的键
for (Integer key : map.keySet()) {
System.out.println("Key = " + key);
}
//遍历map中的值
for (Integer value : map.values()) {
System.out.println("Value = " + value);
}
该方法比entrySet遍历在性能上稍好(快了10%),而且代码更加干净。
方法3--使用Iterator遍历
使用泛型:
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry<Integer, Integer> entry = entries.next();
System.out.print("Key="+entry.getKey()+",Value="+entry.getValue())
}
不使用泛型:
Map map = new HashMap();
Iterator entries = map.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = (Map.Entry) entries.next();
Integer key = (Integer)entry.getKey();
Integer value = (Integer)entry.getValue();
System.out.println("Key = " + key + ", Value = " + value);
}
你也可以在keySet和values上应用同样的方法。
该种方式看起来冗余却有其优点所在。首先,在老版本java中这是惟一遍历map的方式。
另一个好处是,你可以在遍历时调用iterator.remove()来删除entries,另两个方法则不能。
根据javadoc的说明,如果在for-each遍历中尝试使用此方法,结果是不可预测的。从性能方面看,该方法类同于for-each遍历(即方法二)的性能。
方法4、通过键找值遍历(效率低)
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (Integer key : map.keySet()) {
Integer value = map.get(key);
System.out.println("Key = " + key + ", Value = " + value);
}
作为方法一的替代,这个代码看上去更加干净;但实际上它相当慢且无效率。
因为从键取值是耗时的操作(与方法一相比,在不同的Map实现中该方法慢了20%~200%)。如果你安装了FindBugs,它会做出检查并警告你关于哪些是低效率的遍历。所以尽量避免使用。
1、Set集合操作陷阱--- 一个不包含重复元素的 collection。更确切地讲,set 不包含满足 e1.equals(e2) 的元素对 e1 和 e2
public class SetTest2 {
public static void main(String[] args) {
Set<String> set = new HashSet<String>();
set.add("a");
set.add("b");
set.add("c");
set.add("d");
set.add("e");
/**
* 遍历方法一,迭代遍历
*/
for(Iterator<String> iterator = set.iterator();iterator.hasNext();){
System.out.print(iterator.next()+" ");
}
/**
* for增强循环遍历
*/
for(String value : set){
System.out.print(value+" ");
}
}
}
当然Set集合也可以使用for循环了。
注意:这里Set集合中放入的是String类型,假如我们放入一个自己定义的类实例的时候,比如Person类实例,这时候我们要自己重写hashcode和equal方法,
因为当使用HashSet时,会先比较对象的hashCode(),如果存储在集合中的对象的hashCode值是否与增加的对象的hash code值一致;如果不一致,直接加进去;如果一致,再进行equals方法的比较,equals方法如果返回true,表示对象已经加进去了,就不会再增加新的对象,否则加进去。
---Set集合的另外一个重要实现类TreeSet;
TreeSet使用元素的自然顺序对元素进行排序,或者--根据创建 set时提供的Comparator 进行排序,具体取决于使用的构造方法。
通俗一点讲,就是可以按照排序后的列表显示,也可以按照指定的规则排序;
Set<String> set = new TreeSet<String>();
set.add("f");
set.add("a");
set.add("b");
set.add("c");
set.add("d");
set.add("e");
System.out.println(set); 输出:[a, b, c, d, e, f]按照排序后输出。
那么如果我们想让他倒序输出呢?当然方法很多。这里我采用指定一个规则让他倒序输出
public class TreeSetTest3 {
public static void main(String[] args) {
Set<String> set = new TreeSet<String>(new MyComparator());
set.add("a");
set.add("b");
set.add("c");
set.add("d");
set.add("e");
set.add("A");
for(Iterator<String> iterator = set.iterator();iterator.hasNext();){
System.out.print(iterator.next()+" ");
}
}
}
class MyComparator implements Comparator<String>{
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);//降序排列
}
}
输出:e d c b a A
如果Set集合中放入的是我们自己定义的一个类类型呢?
注意:一定要定义一个排序规则类实现Comparator接口,与上面的方法类似,因为TreeSet默认是自然排序,如果没有实现Comparator,那么TreeSet就识别不了。
public class TreeSetTest2 {
public static void main(String[] args) {
Set<Person> set = new TreeSet<Person>(new PersonComparator());
Person p1 = new Person(10);
Person p2 = new Person(20);
Person p3 = new Person(30);
Person p4 = new Person(40);
set.add(p1);
set.add(p2);
set.add(p3);
set.add(p4);
for(Iterator<Person> iterator = set.iterator();iterator.hasNext();){
System.out.print(iterator.next().score+" ");
}
}
}
class Person{
int score;
public Person(int score){
this.score = score;
}
public String toString(){
return String.valueOf(this.score);
}
}
class PersonComparator implements Comparator<Person>{
@Override
public int compare(Person o1, Person o2) {
return o1.score - o2.score;
}
}
输出:10 20 30 40
如果按照一个人的分数的倒序排列,只需要更改compare方法中的o2.score-o1.score
集合--(List、Set、Map)遍历、删除、比较元素时的小陷阱的更多相关文章
- 【Java】集合(List、Set)遍历、判断、删除元素时的小陷阱
开发中,常有场景:遍历集合,依次判断是否符合条件,如符合条件则删除当前元素. 不知不觉中,有些陷阱,不知你有没有犯. 一.漏网之鱼-for循环递增下标方式遍历集合,并删除元素 如果你用for循环递增下 ...
- vector list map 遍历删除指定元素
#include <stdio.h> #include <stdint.h> #include <vector> #include <list> #in ...
- java集合遍历删除指定元素异常分析总结
在使用集合的过程中,我们经常会有遍历集合元素,删除指定的元素的需求,而对于这种需求我们往往使用会犯些小错误,导致程序抛异常或者与预期结果不对,本人很早之前就遇到过这个坑,当时没注意总结,结果前段时间又 ...
- C++ vector 删除一个指定元素 和 find 一个指定元素以及遍历删除、 map遍历删除元素和删除find到的元素
vector: 1.delete element 转载:http://www.cnblogs.com/xudong-bupt/p/3522457.html #include <vector> ...
- STL——遍历 删除 set 元素
==================================声明================================== 本文版权归作者所有. 本文原创,转载必须在正文中显要地注明 ...
- map遍历删除
List<Object> orderManageList = cacheService.values(key); Iterator<Object> it=orderManage ...
- set循环遍历删除特定元素
使用Iterator迭代器 public class Demo { public static void main(String[] args) { Set<Object> obj = n ...
- [JS]应用splice删除多元素时出现的坑
------------------------------------------------------------------------------------- 先看一个片段: var fr ...
- STL中用erase()方法遍历删除元素 .xml
pre{ line-height:1; color:#f0caa6; background-color:#2d161d; font-size:16px;}.sysFunc{color:#e54ae9; ...
随机推荐
- ng-repeat的用法:
-------------------------------------转载: 遍历数组: <li ng-repeat="item in array">{{it ...
- Js 分别取一个数的百位,十位,个位
<!DOCTYPE HTML> <html lang="en-US"> <head> <meta charset="UTF-8& ...
- linux系统常用监控系统状态信息命令
[root@bogon ~]# uptime #平均负载 23:33:21 up 2:09, 3 users, load average: 0.02, 0.05, 0.05 [root@bogon ~ ...
- /dev/null简单入门
2>&1 /dev/null 将标准输入输出全部丢弃(表示2的输出重定向等同于1) 2>filename 把错误信息保存到filename 2>/dev/null >/ ...
- MySQL Binlog常用参数
====================================================================== MySQL Binlog常用参数 log_bin 设置此参 ...
- The Guardian’s Migration from MongoDB to PostgreSQL on Amazon RDS
转载一片mongodb 迁移pg 数据库的文章 原文:https://www.infoq.com/news/2019/01/guardian-mongodb-postgresql The Guardi ...
- 在CentOS 6上使用 AWStats 分析 httpd 和 Tomcat 日志
准备工作: Awstats 是由perl语言编写的,所以要首先准备好awstats的运行环境.# yum install –y perl* Apache 一.首先,要安装apache服务器,并且启 ...
- MySQL 出现 Host is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts'
MySQL 出现 Host is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts' 一大 ...
- TypeScript 之 tsconfig.json
https://m.runoob.com/manual/gitbook/TypeScript/_book/doc/handbook/tsconfig.json.html 如果一个目录下存在一个tsco ...
- Visual Studio Code 使用
VS Code 快捷键: 快捷键 作用 Option+Up 或 Option+Down 上下移动一行 Shift+Option+Up 或 Shift+Option+Down 向上向下复制一行 VS C ...