最近在做文本分析相关的实验,统计词频,情感度量许多数据预处理的工作要用到集合类的遍历,借这个机会也好好复习了一下Java中Map,List的用法。
1. Map的遍历
Map这种集合不能直接取出元素,必须要转换成Set,然后使用Iterator迭代取每一个键值对。以下方法均适用于Map的实现类HashMapHashMap,LinkedHashMap,TreeMap等
方法一:使用EntrySet(我比较常用),可以同时取出key和value
Map<String, String> testMap = new HashMap<String, String>();
Iterator<Entry<String, String>> it = testMap.entrySet().iterator();
Entry<String, String> entry = null;
while(it.hasNext()){
entry = it.next();
System.out.println(entry.getKey() + "\t" + entry.getValue());
}
或者使用for循环,就不需要用到Iterator
for(Map.Entry<String, String> entry: testMap.entrySet()){
System.out.println(entry.getKey() + "\t" + entry.getValue());
}
方法二:使用KeySet,遍历key,然后通过map.get(key)获得对应的value
Map<String, String> testMap = new HashMap<String, String>();
String key = null;
Iterator<String> it = testMap.keySet().iterator();
while(it.hasNext()){
key = it.next();
//System.out.println(it.next() + "\t" + testMap.get(it.next()));
System.out.println(key + "\t" + testMap.get(key));
}
或者使用for循环
String value = null;
for(String key : testMap.keySet()){
value = testMap.get(key);
System.out.println(key + "\t" + value);
}
方法三:如果只需要value,可以:
1. 使用EntrySet遍历value,同上面的方法一
2. 通过keySet遍历key,然后通过map.get(key)得到value,同上面的方法二
3. 通过values直接遍历value
Map<String, String> testMap = new HashMap<String, String>();
Iterator<String> it = testMap.values().iterator();
while(it.hasNext()){
System.out.println(it.next());
}
或者for循环:
for(String value : testMap.values()){
System.out.println(value);
}
遍历Map总结:
1. 如果只需要遍历key,无疑keySet最好,因为EntrySet一次性把key,value都取出来,性能上肯定开销大于只取出key。
2. 如果只想要value,遍历map.values()是最佳的选择,其他EntrySet和KeySet都要绕弯路。
3. 如果要同时取key 和value,这时EntrySet和KeySet则难分绝对优劣。我们知道HashMap是一种数组和链表结合的数据结构。在取value时,首先计算key的hashcode,然后找到数组中对应位置的某一元素,再通过key的equals方法在对应位置的链表中找到需要的元素。虽然EntrySet一次性取出key和value性能开销比只取key大,但是如果这个损失比HashMap查找value的开销小,那么EntrySet更优,否则使用KeySet,通过map.get(key)获取value。
2. List的遍历
方法一:使用普通的for循环
List<String> list = new ArrayList<String>();
for(int i = 0; i < list.size(); i++){
System.out.println(list.get(i));
}
方法二:使用迭代器Iterator
Iterator<String> it = list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
方法三:使用迭代器for-each
for(String str : list){
System.out.println(str);
}
遍历ArrayList总结:
1. ArrayList的底层保存数据还是采用数组Array的方式。所以,遍历访问数组的数据,最快的方式是采用数组的索引,相当于从内存中直接读取数据。因此,使用size效率最高。但是,在多线程时需要考虑并发操作的问题。
2. for-each和Iterator实质是一样的,都是Iterator,for-each只是包装了一下,所以二者的效率是差不多的。但是,使用Iterator中会执行数据锁定,性能稍差。如果在循环过程中去掉某个元素,只能使用iterator.remove()方法,不能使用list.remove()方法,因为会出现并发访问错误。
public class CompareList {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
long startTime = 0L, endTime = 0L;
String temp = null;
//赋值
for(int k = 0; k < 1000000; k++){
list.add("" + k);
}
//方法一:使用size()方法
startTime = System.currentTimeMillis();
for(int i=0;i<list.size();i++) {
temp = list.get(i);
//System.out.println(temp);
}
endTime = System.currentTimeMillis();
System.out.println("使用size,时间为: " + (endTime - startTime) + "mm");
//方法二:使用Iterator
Iterator<String> it = list.iterator();
startTime = System.currentTimeMillis();
while(it.hasNext()){
temp = it.next();
//System.out.println(temp);
}
endTime = System.currentTimeMillis();
System.out.println("使用Iterator,时间为:"+ (endTime - startTime) + "mm");
//方法三:使用for-each
startTime = System.currentTimeMillis();
for(String s : list) {
temp = s;
//System.out.println(temp);
}
endTime = System.currentTimeMillis();
System.out.println("使用for-each,时间为:"+ (endTime - startTime) + "mm");
}
}
实验结果:
使用size,时间为: 12mm
使用Iterator,时间为:14mm
使用for-each,时间为:22mm
多次重复实验,基本都在一个数量级,但是每次都是size最好。
总结的可能不够好,实验也许不科学,欢迎批评指教!