ArrayList的底层是一个数组;
ArrayList类和数组的区别是:ArrayList类数组的长度是可以变化的,而且ArrayList是一个泛型容器;同时ArrayList中只能存储引用类型,而不能存储基本类型;ArrayList类继承至List接口;
原因是:ArrayList数组中存放的是地址。
ArrayList容器的初始大小为10;
ArrayList的特点:
1.查找速度快;
2.增删速度慢;
1. 创建集合对象:ArrayList<String> a = new ArrayList<>(); //右边的<>内可以不写类型;
2. 在对象中添加数据:a.add("list");
3. 在ArrayList中添加一个ArrayList对象:a.addAll(new ArrayList<Integer>(1,2,3,4));
4. 根据索引获取元素:String name = a.get(3);获取第三个位置的元素;
5. 从集合中删除元素:String name = a.remove(4); 从集合中删除第4位的元素;返回值为被删除的元素;
6. 修改元素的值:String name = a.set(2,"A"); 用于替换元素,返回的是被替换的元素;
7. 用于获取数组的尺度:int length = a.size();
如果希望向集合ArrayList当中存储基本数据类型,必须使用基本类型对应的包装类(就是把基本类型转换为引用类型,位于java.lang包下);
ArrayList的线程不安全问题:
1.输出值为null;
elementData[size++] = e;
可以分为两步执行:
1.elementData[size] = e;
2.size++;
当线程1再执行完第一步时,时间片结束,然后线程2执行第一步,此时线程2操作的内存空间和线程1的相同;然后线程1和线程2都执行了一次size++;所以就会导致一个位置被重复执行两次(覆盖的问题),而一个位置没被赋值,为null;
2.数组越界异常;
3.某些线程没有输出值;
ArrayList是线程不安全的:
在多线程的情况下会出现java.util.ConcurrentModificationException的故障;
解决方案:
1.List<String> arr = new ArrayList<>();
List<String> arr1 = Collections.synchronizedList(arr); //加一个同步机制;
2.使用vector容器;
3.使用Java.util.concurrent.CopyOnWriteArrayList;
写时复制:采用了读写分离的思想;也就是在写的时候加锁(再新开辟的内存上加的锁),然后开辟一块新的内存,在新的内存中写入数据,写完之后,用新的内存代替旧的内存;(好处是:在新的内存中写数据,所以其他线程要读数据的话,可以在旧的内存中读取,这样写的时候也可以读,索引被称为读写分离);
代码:
public boolean add(E e) {
synchronized(this.lock) {
Object[] es = this.getArray();
int len = es.length;
es = Arrays.copyOf(es, len + 1);
es[len] = e;
this.setArray(es);
return true;
}
}
ArrayList扩容的问题:
正常境况下,ArrayList初始容量为10,每次扩容1.5倍;扩容的方式为:
int newCapacity = oldCapacity + (oldCapacity >> 1);