重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
- ArrayList和CopyOnWriteArrayList
- CopyOnWriteArrayList详解
源码:
为抚宁等地区用户提供了全套网页设计制作服务,及抚宁网站建设行业解决方案。主营业务为网站建设、成都网站建设、抚宁网站设计,以传统方式定制建设网站,并提供域名空间备案等一条龙服务,秉承以专业、用心的态度为用户提供真诚的服务。我们深信只要达到每一位用户的要求,就会得到认可,从而选择与我们长期合作。这样,我们也可以走得更远!// 增
public boolean add(E e) {
//进行数组容量判断,不够就扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
public void add(int index, E element) {
//检查是否会越界
rangeCheckForAdd(index);
//进行数组容量判断,不够就扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
//将index至数据最后一个元素整体往后移动一格,然后插入新的元素
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
// 删
public E remove(int index) {
//判断是否越界
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
//若该元素不是最后一个元素的话,将index+1至数组最后一个元素整体向前移动一格
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
// 改
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
// 查
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
E elementData(int index) {return (E) elementData[index]; }
ArrayList的底层是数组,所以查询的时候直接根据索引可以很快找到对应的元素,改也是如此,找到index对应元素进行替换。而增加和删除就涉及到数组元素的移动,所以会比较慢。
volatile (挥发物、易变的):变量修饰符,只能用来修饰变量。volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变 化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
transient (暂短的、临时的):修饰符,只能用来修饰字段。在对象序列化的过程中,标记为transient的变量不会被序列化。
源码:
// 增
public boolean add(E e) {
final ReentrantLock lock = this.lock;
//获得锁
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
//复制一个新的数组
Object[] newElements = Arrays.copyOf(elements, len + 1);
//插入新值
newElements[len] = e;
//将新的数组指向原来的引用
setArray(newElements);
return true;
} finally {
//释放锁
lock.unlock();
}
}
public void add(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
if (index > len || index < 0)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+len);
Object[] newElements;
int numMoved = len - index;
if (numMoved == 0)
newElements = Arrays.copyOf(elements, len + 1);
else {
newElements = new Object[len + 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index, newElements, index + 1,
numMoved);
}
newElements[index] = element;
setArray(newElements);
} finally {
lock.unlock();
}
}
// 删
public E remove(int index) {
final ReentrantLock lock = this.lock;
//获得锁
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
E oldValue = get(elements, index);
int numMoved = len - index - 1;
if (numMoved == 0)
//如果删除的元素是最后一个,直接复制该元素前的所有元素到新的数组
setArray(Arrays.copyOf(elements, len - 1));
else {
//创建新的数组
Object[] newElements = new Object[len - 1];
//将index+1至最后一个元素向前移动一格
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index + 1, newElements, index,
numMoved);
setArray(newElements);
}
return oldValue;
} finally {
lock.unlock();
}
}
// 改
public E set(int index, E element) {
final ReentrantLock lock = this.lock;
//获得锁
lock.lock();
try {
Object[] elements = getArray();
E oldValue = get(elements, index);
if (oldValue != element) {
int len = elements.length;
//创建新数组
Object[] newElements = Arrays.copyOf(elements, len);
//替换元素
newElements[index] = element;
//将新数组指向原来的引用
setArray(newElements);
} else {
// Not quite a no-op; ensures volatile write semantics
setArray(elements);
}
return oldValue;
} finally {
//释放锁
lock.unlock();
}
}
// 查
//直接获取index对应的元素
public E get(int index) {return get(getArray(), index);}
private E get(Object[] a, int index) {return (E) a[index];}
增删改都需要获得锁,并且锁只有一把,而读操作不需要获得锁,支持并发。
CopyOnWrite容器有很多优点,但是同时也存在两个问题,即内存占用问题和数据一致性问题。
Vector是增删改查方法都加了synchronized,保证同步,但是每个方法执行的时候都要去获得锁,性能就会大大下降,而CopyOnWriteArrayList 只是在增删改上加锁,但是读不加锁,在读方面的性能就好于Vector,CopyOnWriteArrayList支持读多写少的并发情况。