List集合的特有方法
-
方法介绍
方法名 描述 void add(int index,E element) 在此集合中的指定位置插入指定的元素 E remove(int index) 删除指定索引处的元素,返回被删除的元素 E set(int index,E element) 修改指定索引处的元素,返回被修改的元素 E get(int index) 返回指定索引处的元素
list中的5种遍历方式
细节点注意:
List系列集合中的两个删除的方法
1.直接删除元素2.通过索引进行删除
代码示例:
List<Integer> list = new ArrayList<>();list.add(1);list.add(2);list.add(3);//此方法删除的是1索引上的元素list.remove(1);//此方法删除的是真正的1这个元素Integer i = Integer.valueOf(1);list.remove(i);
ArrayList与数组的区别
-
数组声明了它容纳的元素的类型,而集合不声明。这是由于集合以object形式来存储它们的元素。
-
一个数组实例具有固定的大小,不能伸缩。集合则可根据需要动态改变大小。
ArrayList和LinkedList简介
以下内容为查询结果
ArrayList底层是数组,查询快、增删慢;LinkedList底层是链表,查询慢、增删快;
ArrayList底层是数组,存储空间是连续的,可以根据寻址方式直接找到对应的元素位置,时间复杂度是O(1)。
举例来说:在一条街上,第一家店是001号,那么005号在第五间:
但LinkedList底层是链表,存储空间不连续,需要通过指针关联,在查询过程中需要不断跳转新的地址:
这也是ArrayList比LinkedList查询快的原因。
Java中的原生的数组是不能扩容的,如果初始化时申请了5个元素空间,那么就最多能存5个元素。ArrayList底层也是数组,但是支持动态扩容,所以ArrayList是动态数组:
假设原始容量为5,那么插入新元素时就会扩容,元素拷贝等耗时操作,这就是ArrayList增删慢的原因。但是ArrayList增删元素必然会惩罚扩容和拷贝吗?
插入同理,尾部插入时不涉及元素拷贝。
LinkedList中,理想状态下,链表的增删操作时间复杂度为O(1):
LinkedList集合的特有功能
-
特有方法
方法名 说明 public void addFirst(E e) 在该列表开头插入指定的元素 public void addLast(E e) 将指定的元素追加到此列表的末尾 public E getFirst() 返回此列表中的第一个元素 public E getLast() 返回此列表中的最后一个元素 public E removeFirst() 从此列表中删除并返回第一个元素 public E removeLast() 从此列表中删除并返回最后一个元素
问题
1 ArrayList如何添加元素?
-
扩容:往ArryList中添加元素的时候,会首先检查是否需要扩容。当size == elementData.length时,表示数据数量已经超过了数组容量,需要扩容,扩容后的数组的长度为原来数组长度的1.5倍;
-
复制:当扩容检查完毕后,如果添加的元素不在数组尾部,则将索引后面的元素通过System.arraycopy往后移动一位;
-
赋值:将值赋给数组中的对应索引,并将size++;
如果此时ArrayList的长度为size,在多线程运行的情况下,线程A想要将元素存放在索引为index的位置上,但此时CPU暂停线程A的调度,线程B得到运行的机会,也是向index的位置上添加元素。之后线程A和线程B都继续运行,都会增加size的值,这样数组的长度就变成了size + 2,这样就线程不安全了。
2 ArrayList是否能无限添加元素?会抛出异常吗?
可以无限添加,不会抛出异常。ArrayList会自动为其扩容,扩容后的大小是int newCapacity = (oldCapacity * 3) / 2 + 1。
3 ArrayList和LinkedList的时间复杂度?
ArrayList是线性表(数组):
add(E e):在数组尾部添加元素,时间复杂度为O(1); add(int index, E element):在索引为index的位置添加元素,需要后面的元素后移,时间复杂度为O(n); remove(int index)/remove(Object o):删除元素,需要后面的元素后移,时间复杂度为O(n); set(int index, E element):修改元素,时间复杂度为O(1); get(int index):获取索引为index的元素,时间复杂度为O(1); LinkedList是链表操作:
add(E e):在数组尾部添加元素,时间复杂度为O(1); add(int index, E element):在索引为index的位置添加元素,指针指向操作,时间复杂度为O(1); remove(int index)/remove(Object o):删除元素,指针指向操作,时间复杂度为O(1) set(int index, E element):修改元素,时间复杂度为O(n); get(int index):获取索引为index的元素,时间复杂度为O(n);
4 ArrayList线程安全吗?为什么?如何解决多线程问题?
ArrayList线程不安全,因为相关的操作方法没有做同步,操作没有原子性,在多线程环境下会出现变量的读写异常。比如size++是非原子性的,如果两个线程同时执行,两个线程分别读了size的值,再分别执行size++,最后size的值变成了size + 1而不是size + 2。
多线程环境下使用CopyOnWriteArrayList保证线程安全,活着使用Collections.synchronizedList(list),或者给多线程的操作加锁,或者使用Vector。