在介绍常见集合类之前先给大家看看这个UML图(就如同设计师需要看懂设计稿一样)
注意:List、Set、Map没有实现同一个接口 Map是一个单独的接口。它是键值对类型先有键(主键)再有值的
什么是UML?
UML是统一建模语言,是一种可视化的面向对象建模语言,是一种用来对真实世界物理进行建模的标准标记,用图形方式表现典型的面向对象系统的整个结构。它的作用域不局限于支持面向对象的分析与设计,还支持从需求分析开始的软件开发的全过程。
为了更好的看懂类图,大家可以点进下方链接了解了解UML类图几种关系的总结
UML类图几种关系的总结http://www.uml.org.cn/oobject/201609062.asp
List集合
特点:元素有序,且可重复
遍历:下标,foreach,迭代器
List<Integer> list=new ArrayList<Integer>();
@Before
public void setup() {
list.add(1);
list.add(2);
list.add(3);
list.add(3);
list.add(4);
}
/**
* foreach遍历
*/
@Test
public void list01() {
for(Integer e:list){
System.out.println(e);
}
}
/**
* for循环
*/
@Test
public void list02() {
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
}
/**
* 迭代器
*/
@Test
public void list03() {
Iterator<Integer> it = list.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
扩容:
- 初始容量10,负载因子0.5,扩容增量0.5倍
- 新容量 = 原容量 + 原容量 * 0.5 , 如 ArrayList的容量为10,一次扩容后是容量为15
代码示例:
public void listKR()throws Exception {
List<Integer> list=new ArrayList<Integer>();
for(int i=0;i<=100;i++) {
list.add(i);
System.out.println("i :"+i);
System.out.println("length :"+getListElsSize(list));
}
}
private int getListElsSize(List obj)throws Exception {
Class<? extends List> clazz = obj.getClass();
Field f = clazz.getDeclaredField("elementData");
f.setAccessible(true);
Object[] object =(Object[]) f.get(obj);
return object.length;
}
效果如下:
实现
🟡ArrayList:是一个简单数据结构,具有超出容量自动扩容,动态数组的特点。内部实现是基于基础的对象数组的。随机访问快但是不适合随机增加或删除。线程不安全
🟡LinkedList:LinkedList提供额外的get,remove,insert方法在LinkedList的首部或尾部。LinkedList可被用作堆栈(stack)【包括了push,pop方法】,队列(queue)或双向队列(deque)。适合做随机的增加或删除。以双向链表实现,链表无容量限制,允许元素为null,线程不安全。
🟡Vector:是线程安全的,但是并行性能慢,不建议使用
🟡CopyOnWriteArrayList:写时复制出一个新的数组,完成插入、修改或者移除操作后将新数组赋值给array,适合于读多,写少的场景。比Vector性能高,最终一致性。实现了List接口,使用方式与ArrayList类似。线程安全
ArrayList remove(删除) 的注意点 ❗❗❗
题目要求:删除集合中所有为3的元素
数据准备:为方便演示,需要有紧挨在一起的两个或多个相同的元素
List<Integer> list=new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(3);
list.add(4);
1️⃣第一种:for循环遍历list删除指定元素❎
public void remove01() {
for(int i=0;i<list.size();i++){
if(list.get(i)==3)
list.remove(i);
}
System.out.println(list);
}
效果图如下:
很显然只删除了一个3,这是为什么呢?因为当删除到元素3的时候下面元素的位置会往上移那么原先第二个3的位置就成4了 所以没有删除成功
图形解读:
2️⃣第二种:for循环遍历list删除时将i--✅
public void remove02() {
for(int i=0;i<list.size();i++){
if(list.get(i)==3)
list.remove(i--);
}
System.out.println(list);
}
效果图如下:
当删除第一个3时,3后面元素都会向上移动,而当前index等于2,后一个3会移除到index=2的位置,又因为index-1被跳回去了 所以删除成功!!!
3️⃣第三种:倒序遍历list是✅
public void remove03() {
for(int i=list.size()-1;i>=0;i--){
if(list.get(i)==3){
list.remove(i);
}
}
System.out.println(list);
}
效果图如下:
4️⃣第四种:forEach遍历list删除 ❎
public void remove04() {
for(Integer i:list){
if(i==3) list.remove(i);
}
System.out.println(list);
}
效果图如下:
报错显示:java.util.ConcurrentModificationException (当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。)
将方法点进去查看源码会发现在next、remove方法中都会调用checkForConmodification的方法。
若modCount不等于expectedModCount 就会报ConcurrentModificationException
但是在删除的时候是通过ArrayList的remove方法去操作的,不是Itr内部的那个删除方法去操作的。remove()函数会调用fastRemove()函数,使得modCount的值自增1.然而,for循环下一次调用Itr.next(),Itr.next()调用Itr.checkForComodification()时,会发现,modCount和expectedModCount两个值不相等!因为在这个删除操作的过程中没有对expectedModCount重新赋值,所以就抛出异常了。
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // Let gc do its work
}
那为什么删除倒数第二个会成功呢?
为了达到效果,在原本数据中添加一条数据
List<Integer> list=new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(3);
list.add(5);
list.add(4);
方法
public void remove04() {
for(Integer i:list){
if(i==5)
list.remove(i);
}
System.out.println(list);
}
通过查看源码,刚开始cursor开始值是默认为0,0不等于size为true,将会进入到next()方法中,cursor会加一,当执行到5的时候,cursor等于5,5被删除了,并且size从原本的6变为5了,这个时候cursor等于size,就不会进入next()方法了。
源码如下:
1.It gets the iterator.
2.Checks for hasNext().
public boolean hasNext()
{
return cursor != size(); // cursor is zero initially.
}
3.If true, gets the next element using next().
public E next()
{
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
final void checkForComodification()
{
// Initially modCount = expectedModCount (our case 5)
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
5️⃣第五种:通过迭代器进行删除1✅
public void remove05() {
Iterator<Integer> it=list.iterator();
while(it.hasNext()){
if(it.next()==3){
it.remove();
}
}
System.out.println(list);
}
6️⃣第六种::通过迭代器进行删除2❎
public void remove06() {
Iterator<Integer> it=list.iterator();
while(it.hasNext()){
Integer value=it.next();
if(value==3){
list.remove(value);
}
}
}
效果图如下:
报错显示:java.util.ConcurrentModificationException (当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。)原因查看第四种方法
删除的参数是基本型的整数
public void remove07() {
list.remove(2);
System.out.println(list);
}
效果图如下:
若想删除对象类型的2 使用Integer.valueOf() 基本类型的2封装成对象类型的2
public void remove07() {
list.remove(Integer.valueOf(2));//基本类型的2封装成对象类型的2
System.out.println(list);
}
效果图如下