Vector,LinkedList,修改同时并发迭代访问的问题,对象赋值和克隆
今日主要总结前一天没有写完的List集合中Vecto和LilkedLIst的后续总结,以及修改同时并发迭代访问的问题和一个线程使用迭代器访问数据,另外一个线程修改数据时会产生的错误,最后总结了克隆,浅克隆和深克隆的区别以及不同实现。
Vector
类定义
属于老版本提供的,从1.0,而ArrayList比较新,从1.2。属于线程安全的类,大部分方法上都有synchronized,一般用于要求线程安全的属性定义
public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable,
java.io.Serializable
数据存储
protected Object[] elementData; 采用也是数组的方式存储数据
构造器方法
public Vector() {
this(10); //表示调用当前类的其它构造器,初始化容积为10,增长的步长值为0
}
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
public Vector(int initialCapacity, int capacityIncrement) { //参数1是初始化容积,参数2是容
积增长的步长值
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
this.elementData = new Object[initialCapacity];//按照初始化容积值构建对应的数组
this.capacityIncrement = capacityIncrement;
}
add新增元素的方法实现
public synchronized boolean add(E e) {//线程安全的方法
modCount++; //修改次数+1
ensureCapacityHelper(elementCount + 1); //处理容积
elementData[elementCount++] = e; //在数组中存储元素
return true;
}
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0) //需要储存的数据超出数组可以存放的数据格
式,则需要进行增长
grow(minCapacity);
}
private void grow(int minCapacity) {
int oldCapacity = elementData.length; //当前数组的长度,也就是可以存放的元素个数
int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement :
oldCapacity); //新长度为原始长度的2倍或者原始长度+步长值
if (newCapacity - minCapacity < 0) //如果新长度不满足最小长度要求,则新长度为最小要
求的长度
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0) //如果新长度大于最大数组长度进行长度处理
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity); //将原始数组中的数据拷贝到新
数组中,并替换原始数组
}
private static int hugeCapacity(int minCapacity) { //和ArrayList处理一致
if (minCapacity < 0) //OOM 内存溢出
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
- 方法同步
- 增长为2倍
- 无参数创建时是10个长的数组
删除元素的方法remove(Object)
p
ublic boolean remove(Object o) {
return removeElement(o);
}
public synchronized boolean removeElement(Object obj) {
modCount++;
int i = indexOf(obj); //查找元素的索引值
if (i >= 0) {
removeElementAt(i); //按照索引删除指定元素
return true;
}
return false;
}
public synchronized void removeElementAt(int index) {
modCount++;
//针对index索引值进行合法性验证
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " + elementCount);
} else if (index < 0) {
throw new ArrayIndexOutOfBoundsException(index);
}
int j = elementCount - index - 1; //获取需要移动的元素个数
if (j > 0) { //通过拷贝的方式将数据的后续移动元素向前移动一位
System.arraycopy(elementData, index + 1, elementData, index, j);
}
elementCount--; //元素个数-1
elementData[elementCount] = null; //将数组末尾的元素值赋null
}
LinkedList
类定义
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>,
Cloneable, java.io.Serializable
//Deque是队列接口,提供一个双端队列的访问方法实现
底层实现为双向链表
private static class Node<E> { //节点定义
E item; //具体存储的数据
Node<E> next; //向后的指针
Node<E> prev; //向前的指针
}
LinkedList类中的数据存储
transient Node<E> first; //头指针,指向链表的第一个元素
transient Node<E> last; //尾指针,指向链表的最后一个元素
对应的构造器
public LinkedList() { //没有初始化容积
}
add方法中
- 创建Node对象,其中包含需要添加的元素值
- Node对象的next为null
- prev指向last
- last对象的next为新建对象Node
- l ast指向新建的Node对象
List总结
修改同时并发迭代访问的问题
需求:一个线程使用Iterator迭代访问集合中的元素,另外一个线程修改集合中的元素。临界资源为集合
public class T4 {
public static void main(String[] args)throws Exception {
List list = new ArrayList();
for(int i=0;i<5;i++)
list.add(i);
new Thread(()->{
System.out.println(Thread.currentThread().getName()+",begin....");
Iterator it=list.iterator();
while(it.hasNext()) {
Object tmp=it.next();
System.out.println(tmp);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+",end....");
}).start();
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+",begin modify....");
list.remove(0);
System.out.println(Thread.currentThread().getName()+",end modify....");
}
}
ConcurrentModicationException
一个线程使用迭代器访问数据,另外一个线程修改数据
- 如果仅仅只是修改集合中的元素值,可以正常执行
- 如果修改了集合的结构则会报错ConcurrentModicationException add/remove
ArrayList中提供了Iterator接口的私有实现
private class Itr implements Iterator<E> {
int expectedModCount = modCount; 当构建迭代器对象时获取当前集合的修改次数
报错的调用方法
@SuppressWarnings("unchecked")
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();
}
快死异常主要用于检查遍历数据时的并发修改操作。主要作用是杜绝在遍历元素时其它线程修改了集合框架结构
对象赋值和克隆
对象赋值 Date now2=now;
两个变量now2和now中存放是同一个对象的地址,不管操作那个变量两个都受影响,因为now2和now是同一个对象
如果希望两个变量相互不影响,则只能通过克隆实现。Java中的克隆可以分为深克隆和浅克隆
public class Student{
private long id;
private Date birth;
}
如果需要支持克隆操作要求必须实现Cloneable接口
Object类中提供了clone()方法,这个方法是一个本地方法,是由虚拟机提供实现的
protected native Object clone() throws CloneNotSupportedException;
Cloneable接口属于标志性接口,没有具体的实现,具体实现实际上是Object类中提供的
- 自定义类上要求实现Cloneable接口
- 自定义类中要求实现clone方法,这个方法只需要调用父类的clone方法即可,一般不需要额外的处理
public class Test2 {
public static void main(String[] args) throws Exception {
Student s1 = new Student();
s1.setId(100L);s1.setBirth(new Date());
//浅克隆,只是克隆s1对象,但是针对s1中的引用类型属性只是克隆地址
Student s2 = (Student) s1.clone();
s2.getBirth().setYear(9000);
System.out.println(s2);
System.out.println(s1);
}
}
class Student implements Cloneable {
private Long id;
private Date birth;
public Object clone() throws CloneNotSupportedException{ //定义该方法的原因是Object中的
clone方法是protected的
return super.clone();
}
@Override
public String toString() {
return "Student [id=" + id + ", birth=" + birth + "]";
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
}
深克隆
一般采用对象流实现。自定义类要求实现Serializable接口
public class Test2 {
public static void main(String[] args) throws Exception {
Student s1 = new Student();
s1.setId(100L);s1.setBirth(new Date());
//浅克隆,只是克隆s1对象,但是针对s1中的引用类型属性只是克隆地址
Student s2 = (Student) s1.deepClone();
s2.getBirth().setYear(9000);
System.out.println(s2);
System.out.println(s1);
}
}
class Student implements Serializable {
private Long id;
private Date birth; //其中的属性也必须实现了序列化接口
private transient String password;//transient表示该属性不能被序列化处理
public Student deepClone() {
Student res=null;
try {
ByteArrayOutputStream baos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(baos);
oos.writeObject(this);
ObjectInputStream ois=new ObjectInputStream(new
ByteArrayInputStream(baos.toByteArray()));
res=(Student)ois.readObject();
oos.close();
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
return res;
}
@Override
public String toString() {
return "Student [id=" + id + ", birth=" + birth + "]";
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
}