迭代器
在使用iterator()时会经常遇到ConcurrentModificationException异常,这通常是由于在使用Iterator遍历的同时又使用Collection.add对容器做增加或者删除的操作所导致的,或者由于多线程操作导致的。
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
当调用iterator()返回Iterator对象时,expectedModCount等于此刻容器中元素的个数。在调用next()时会比较modCount != expectedModCount。因此在使用Iterator遍历容器的过程中,如果对容器进去增加或删除操作,就会改变容器中元素的数量,即modCount。
解决方法:
- 使用ListIterator.add
- 使用Iterator.remove或ListIterator.remove
- 遍历过程中将需要删除的对象保存到另一个集合,遍历结束后调用removeAll()
多线程访问容器的过程中抛出ConcurrentModificationException解决方法:
- 使用线程安全的容器,比如ConcurrentHashMap和CopyOnWriteArrayList
- 在使用迭代器遍历容器时对容器的操作放到synchronized代码中,但是当引用程序并发程度比较高时,会严重影响程序的性能。
ArrayList、Vector和LinkedList
Vector是线程安全的,ArrayList不是线程安全的,所以Vector性能上略逊于ArrayList。
HashMap、Hashtable、TreeMap、LinkedHashMap和WeakHashMap
HashMap和Hashtable都采用hash法进行索引,主要区别:
- HashMap非线程安全,允许空键值,但最多只允许一条记录的键为null。
- HashMap把Hashtable的contains方法去掉了,改成containsValue和containsKey。Hashtable继承自Dictionary类。
- Hashtable的方法是线程安全的。
- Hashtable中Hash数组默认大小是11,增加的方式是old*2+1。在HashMap中,Hash数组默认大小是16,而且一定是2的指数。
- 两者采用的hash/rehash算法几乎一样。
- hash值的使用不同,Hashtable直接使用对象的hashCode。
WeakHashMap中key采用的是“弱引用”的方式,只要key不再被外部引用,它就可以被垃圾回收器回收。而HashMap中key采用“强引用的方式”,只有key从HashMap中删除后,才会被回收。
如何实现HashMap的同步?
Map map1 = Collections.synchronizedMap(new HashMap<>());
用自定义类型作为HashMap或Hashtable的key
重写key对象的hashCode()和equals()方法
如果两个对象相等,则其hashCode也相等
package containers;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* Created by mook on 2017/5/3.
*/
class Person {
String id;
String name;
public Person(String id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "id= " + id + ",name= " + name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
if (id != null ? !id.equals(person.id) : person.id != null) return false;
return name != null ? name.equals(person.name) : person.name == null;
}
@Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (name != null ? name.hashCode() : 0);
return result;
}
}
public class mapKey {
public static void test() {
Map<String,String> map = new HashMap<>();
map.put(null,null);
map.put("abc",null);
map.put("zxc","ni");
map.put("zxc","huai");
System.out.println(map);
Iterator iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry entry = (Map.Entry) iterator.next();
String key = (String) entry.getKey();
String value = (String) entry.getValue();
System.out.println(key + " " + value);
}
}
public static void testPerson() {
Map<Person,String> map = new HashMap<>();
Person p1 = new Person("111", "rose");
Person p2 = new Person("111", "rose");
map.put(null,null);
map.put(p1, "address1");
map.put(p2, "address2");
System.out.println(map);
Iterator iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry entry = (Map.Entry) iterator.next();
Person key = (Person) entry.getKey();
String value = (String) entry.getValue();
System.out.println(key + " " + value);
}
}
public static void main(String[] args) {
test();
testPerson();
}
}
**output:**
{null=null, abc=null, zxc=huai}
null null
abc null
zxc huai
{null=null, id= 111,name= rose=address2}
null null
id= 111,name= rose address2