集合之自结

11、Map排序(TreeMap的key排序,TreeMap的value排序;HashMap的value排序;)

12、简单介绍Map

13.判断List、Map、Set是否为空及效率比较 

17、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
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,它会做出检查并警告你关于哪些是低效率的遍历。所以尽量避免使用。

16,Map集合遍历的4中方法?

既然java中的所有map都实现了Map接口,以下方法适用于任何map实现(HashMap, TreeMap, LinkedHashMap, Hashtable, 等等)

方法一 在for-each循环中使用entries来遍历

这是最常见的并且在大多数情况下也是最可取的遍历方式。在键值都需要时使用。 

  1. Map<Integer, Integer> map = new HashMap<Integer, Integer>();  
  2. for (Map.Entry<Integer, Integer> entry : map.entrySet()) {  
  3.     System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());  
  4. }  

注意:for-each循环在Java 5中被引入所以该方法只能应用于java 5或更高的版本中。如果你遍历的是一个空的map对象,for-each循环将抛出NullPointerException,因此在遍历前你总是应该检查空引用。

方法二  在for-each循环中遍历keys或values。

如果只需要map中的键或者值,你可以通过keySet或values来实现遍历,而不是用entrySet。

  1. Map<Integer, Integer> map = new HashMap<Integer, Integer>();  
  2. //遍历map中的键  
  3. for (Integer key : map.keySet()) {  
  4.     System.out.println("Key = " + key);  
  5. }  
  6. //遍历map中的值  
  7. for (Integer value : map.values()) {  
  8.     System.out.println("Value = " + value);  
  9. }  

该方法比entrySet遍历在性能上稍好(快了10%),而且代码更加干净。 

方法三使用Iterator遍历 

使用泛型:

  1. Map<Integer, Integer> map = new HashMap<Integer, Integer>();  
  2. Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator();  
  3. while (entries.hasNext()) {  
  4.     Map.Entry<Integer, Integer> entry = entries.next();  
  5.     System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());  
  6. }  

不使用泛型:

  1. Map map = new HashMap();  
  2. Iterator entries = map.entrySet().iterator();   
  3. while (entries.hasNext()) {  
  4.     Map.Entry entry = (Map.Entry) entries.next();  
  5.     Integer key = (Integer)entry.getKey();  
  6.     Integer value = (Integer)entry.getValue();  
  7.     System.out.println("Key = " + key + ", Value = " + value);  
  8. }  

你也可以在keySet和values上应用同样的方法。

该种方式看起来冗余却有其优点所在。首先,在老版本java中这是惟一遍历map的方式。另一个好处是,你可以在遍历时调用iterator.remove()来删除entries,另两个方法则不能。根据javadoc的说明,如果在for-each遍历中尝试使用此方法,结果是不可预测的。

从性能方面看,该方法类同于for-each遍历(即方法二)的性能。 

方法四、通过键找值遍历(效率低)

  1. Map<Integer, Integer> map = new HashMap<Integer, Integer>();  
  2. for (Integer key : map.keySet()) {  
  3.      Integer value = map.get(key);  
  4.     System.out.println("Key = " + key + ", Value = " + value);  
  5. }  

作为方法一的替代,这个代码看上去更加干净;但实际上它相当慢且无效率。因为从键取值是耗时的操作(与方法一相比,在不同的Map实现中该方法慢了20%~200%)。如果你安装了FindBugs,它会做出检查并警告你关于哪些是低效率的遍历。所以尽量避免使用。

总结

如果仅需要键(keys)或值(values)使用方法二。如果你使用的语言版本低于java 5,或是打算在遍历时删除entries,必须使用方法三。否则使用方法一(键值都要)。 

12、简单介绍Map

map是键值对的集合接口,它的实现类主要包括:HashMap,TreeMap,Hashtable以及LinkedHashMap等。
--HashMap:
非同步的。最常用的Map,它根据key的HashCode 值来存储数据,根据key可以直接获取它的Value,同时它具有很快的访问速度。
HashMap最多只允许一条记录的key值为Null(多条会覆盖);允许多条记录的Value为 Null。

--TreeMap:
非同步的。能够把它保存的记录根据key排序,默认是按升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。TreeMap不允许key的值为null。

--Hashtable:
类似HashMap,不同的是:key和value的值均不允许为null;它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢。

--LinkedHashMap:
非同步的。保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的,在遍历的时候会比HashMap慢。key和value均允许为空。

11.、Map排序(TreeMap的key排序,TreeMap的value排序;HashMap的value排序)

11.1 TreeMap--TreeMap的key排序;
TreeMap默认是升序的,如果我们需要改变排序方式,则需要使用比较器:Comparator。

Comparator可以对集合对象或者数组进行排序的比较器接口,实现该接口的public compare(T o1,To2)方法即可实现排序,
该方法主要是根据第一个参数o1,小于、等于或者大于o2分别返回负整数、0或者正整数。

public class TreeMapTest {
public static void main(String[] args) {
Map<String, String> map = new TreeMap<String, String>(
new Comparator<String>() {
public int compare(String obj1, String obj2) {
 // 降序排序
 return obj2.compareTo(obj1);
}
});
map.put("c", "ccccc");
map.put("a", "aaaaa");
map.put("b", "bbbbb");
map.put("d", "ddddd");

Set<String> keySet = map.keySet();
Iterator<String> iter = keySet.iterator();
while (iter.hasNext()) {
String key = iter.next();
System.out.println(key + ":" + map.get(key));
}
}
}
运行结果如下:
d:ddddd
c:ccccc
b:bbbbb
a:aaaaa
上面例子是对根据TreeMap的key值来进行排序的,但是有时我们需要根据TreeMap的value来进行排序。

11.2 TreeMap的value排序

对value排序我们借助于Collections的sort(List<T> list, Comparator<? super T> c)方法,该方法根据指定比较器产生的顺序对指定列表进行排序。但是有一个前提条件,那就是所有的元素都必须能够根据所提供的比较器来进行比较。

public class TreeMapTest {
public static void main(String[] args) {
Map<String, String> map = new TreeMap<String, String>();
map.put("d", "ddddd");
map.put("b", "bbbbb");
map.put("a", "aaaaa");
map.put("c", "ccccc");

//这里将map.entrySet()转换成list
List<Map.Entry<String,String>> list = new ArrayList<Map.Entry<String,String>>(map.entrySet());
//然后通过比较器来实现排序
Collections.sort(list,new Comparator<Map.Entry<String,String>>() {
//升序排序
public int compare(Entry<String, String> o1,Entry<String, String> o2) {
return o1.getValue().compareTo(o2.getValue());
}
});

for(Map.Entry<String,String> mapping:list){
   System.out.println(mapping.getKey()+":"+mapping.getValue());
}
}
}
运行结果
a:aaaaa
b:bbbbb
c:ccccc
d:ddddd

11.3 HashMap--HashMap的value排序;

我们都是HashMap的值是没有顺序的,他是按照key的HashCode来实现的。对于这个无序的HashMap我们要怎么来实现排序呢?
参照TreeMap的value排序,我们一样的也可以实现HashMap的value排序。

public class HashMapTest {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("c", "ccccc");
map.put("a", "aaaaa");
map.put("b", "bbbbb");
map.put("d", "ddddd");

List<Map.Entry<String,String>> list = new ArrayList<Map.Entry<String,String>>(map.entrySet());
Collections.sort(list,new Comparator<Map.Entry<String,String>>() {
//升序排序
public int compare(Entry<String, String> o1,Entry<String, String> o2) {
   return o1.getValue().compareTo(o2.getValue());
}
});

for(Map.Entry<String,String> mapping:list){
  System.out.println(mapping.getKey()+":"+mapping.getValue());
}
}
}
运行结果
a:aaaaa
b:bbbbb
c:ccccc
d:ddddd

12. Java:集合与数组转换?

List,Set转换为数组的方法。
toArray函数有两种形式,一种无参数,一种带参数,注意带参数形式中,要指明数组的大小。

public void convertCollectionToArray() {
List list = new ArrayList();
Object[] objectArray1 = list.toArray();
String[] array1 = list.toArray(new String[list.size()]);

 Set set = new HashSet();
 Object[] objectArray2 = set.toArray();
 String[] array2 = set.toArray(new String[set.size()]);
}

反过来,数组转换为List,Set。
Integer[] numbers = {7, 7, 8, 9, 10, 8, 8, 9, 6, 5, 4};
// To convert an array into a Set first we convert it to a List. Next
// with the list we create a HashSet and pass the list as the constructor.
List list = Arrays.asList(numbers);
Set set = new HashSet(list);

注意:对于int[]数组不能直接这样做,因为asList()方法的参数必须是对象。应该先把int[]转化为Integer[]。对于其他primitive类型的数组也是如此,必须先转换成相应的wrapper类型数组。

int[] numbers = {7, 7, 8, 9, 10, 8, 8, 9, 6, 5, 4};
int size = numbers.length;
Integer[] array = new Integer[size];
for (int i = 0; i < numbers.length; i++) {
Integer integer = numbers[i];
array[i] = integer;
}
List list = Arrays.asList(array);

11.判断List、Map、Set是否为空及效率比较:

//如果object为null,则设置为defaultValue
ObjectUtils.defaultIfNull(object, defaultValue);

//判断集合是否为null
List<String> list=new ArrayList<String>();
System.out.println(list.isEmpty()); //true
System.out.println(list.size()); //0

Set<String> set=new HashSet<String>();
System.out.println(set.isEmpty()); //true
System.out.println(set.size()); //0

Map<String, String> map=new HashMap<String, String>();
System.out.println(map.isEmpty()); //true
System.out.println(map.size()); //0

方法一(数据量大,效率低):
if(list!=null && list.size()>0){}

方法二(数据量大,效率高):
if(list!=null && !list.isEmpty()){}

15,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);
  }
}
}

15.1、漏网之鱼-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”的内容):

15.2、对于此情况,我一般都从后面开始遍历,以避免问题:

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);
}
}

15.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。

11、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,表示对象已经加进去了,就不会再增加新的对象,否则加进去。

2)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

posted on 2017-06-28 20:23  左手指月  阅读(170)  评论(0编辑  收藏  举报