01※、Java中的集合(数据结构)、List集合的使用、 Iterator迭代器的源码以及ArrayList的工作原理、继承链和实现接口

本文详细介绍了Java中的ArrayList集合,包括其作为线性表的特点、如何添加和删除元素、扩容机制以及工作原理。重点讲解了ArrayList的初始化、add方法、扩容策略(默认1.5倍增长)和避免并发修改异常的方法。此外,还探讨了ArrayList的继承链和相关接口实现。
摘要由CSDN通过智能技术生成

※Java中的集合(数据结构)

--数组-线性表

--链表

--栈

--队列

--散列表

--二叉树

--映射关系(key-value)

3ee3733b4cb541bbae62d434d9263110.png

 

 

List集合:

ArrayList:ArrayList是线程不安全的、物理地址上是连续的

--有序的、可重复的、线性结构的(线性表)

List的工作原理:底层是通过数组来实现的,当存储的数据到达一定的个数的时候,会扩充集合的容量

1、创建ArrayList是调用了无参构造器,默认初始化为空的对象数组
	Object[] elementData={};

2、add添加数据的方法
        ensureCapacityInternal(size + 1);  【确保容量是足够的】
	--判断是否为空的数组、一开始的时候先初始化长度为10
	--判断是否溢出,如果是则进行扩容,1.5倍的长度的扩容
		通过数组工具类中的copyOf方法扩容
        elementData[size++] = e;//赋值
        return true;//返回true

1、创建ArrayList是调用了无参构造器,默认初始化为空的对象数组 Object[] elementData={}; 2、add添加数据的方法 ensureCapacityInternal(size + 1); 【确保容量是足够的】 --判断是否为空的数组、一开始的时候先初始化长度为10 --判断是否溢出,如果是则进行扩容,1.5倍的长度的扩容 通过数组工具类中的copyOf方法扩容 elementData[size++] = e;//赋值 return true;//返回true

//注意:所有的集合中存放的数据都是引用类型

92e1388414284672bb56db57bdbdb568.png

--List集合的使用

/**
 * @author Lantzrung
 * @date 2022年7月25日
 * @Description
 */
package com.day0725;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class ListDemo1 {
	private static Collection c;

	public static void main(String[] args) {
		// 数组:声明类型 [声明数组长度]
		Object[] arr = new Object[5];
		arr[0] = "abc";
		arr[1] = 1;

		// 注意:这里有警告是因为没有限定类型,<>是泛型的
		// 注意:这里的1是包装类不是int而是Integer

		// List【有序的、可重复】
		// 注意:List在集合中存放的不是基本数据类型,而是Object引用类型
		// 1、ArrayList数组默认存储数据是【10】【36】、

		// Collection是最大的接口
//	 	Collection/*父接口*/ c = new List();//因为List本身是一个接口,所以它们本身不能被实例化
		// ctrl放到鼠标List
		// 以下是List的部分源码
//		public interface List<E> extends Collection<E> {
//		    // Query Operations
//		    /**
//		     * Returns the number of elements in this list.  If this list contains
//		     * more than <tt>Integer.MAX_VALUE</tt> elements, returns
//		     * <tt>Integer.MAX_VALUE</tt>.
//		     *
//		     * @return the number of elements in this list
//		     */
//		}

//	 	Collection c = new ArrayList();

		// 在List源码中 对着list按住ctrl键再点击Open Implementation可以查看实现类的接口 如常用的ArrayList<E>

		// 定义一个容器可以存放多个数据,并且数据是可变的【集合】【功能方便、强大】
		// List【有序的、可重复】 注意:在集合中存放的不是基本数据类型,而是Object引用

		// 操作一:
		List l = new ArrayList();
		// 添加数据
		l.add(1);// 注意:这里的1是包装类不是int而是Integer
		l.add(2);//
		l.add(3);
		// 输出数据
		System.out.println(l);// [1, 2, 3]

		// 指定索引插入数据
		l.add(1, "abc");

		// 输出数据
		System.out.println(l);// [1, abc, 2, 3]

		// 操作二:
		// 创建第二个对象
		List l1 = new ArrayList<>();
		// 添加数据
		l1.add(1);// 不是int,而是Integer
		l1.add(11);
		l1.add(12);
		l1.add(13);
		// 将另一组的集合数据添加进来
		l1.addAll(l);
		System.out.println(l);// [1, abc, 2, 3]
		System.out.println(l1);// [11, 12, 13, 1, abc, 2, 3]

//		// 注意:要是反过来添加的话
//		l.addAll(l1);
//		System.out.println(l1);// [11, 12, 13]
//		System.out.println(l);// [1, abc, 2, 3, 11, 12, 13]

		// 操作三:
		// 获取数据的索引位置
		List l2 = new ArrayList<>();
		// 添加数据
		l2.add(1);
		l2.add(2);
		l2.add(3);

		// 根据索引进行随机获取
		System.out.println(l2.get(1));// 2

		// 获取数据的索引 方式一: 获取的是该集合中最前面的索引位置
		System.out.println(l2.indexOf(2));// 1

		// 获取数据的索引 方式二:
		System.out.println(l2.lastIndexOf(2));// 1 就是访问当前2的索引位置在哪里就是在1中

		// 操作四:
		// 添加索引值
		l2.add(2);
		System.out.println(l2.indexOf(2));// 1 获取的是该集合中最前面的索引位置
		System.out.println(l2.lastIndexOf(2));// 3 就是访问当前集合中的最后一个引用出现的 
        //索引位置

		// 替换数据 【根据索引位置进行替换】
		l2.set(1, "B");
		System.out.println(l2);// [1, B, 3, 2]

		// 操作五:
		List l3 = new ArrayList();
		// 添加数据
		l3.add("a");
		l3.add("b");
		l3.add("c");
		l3.add("b");
		// 根据索引进行随机获取
		System.out.println(l3.get(1)); // b
		// 获取数据的索引 方式一: 获取的是该集合中最前面的索引位置
		System.out.println(l3.indexOf(2));// -1 要是找不到该数据的索引位置则为-1
		System.out.println(l3.indexOf("b"));// 1
		// 获取数据的索引 方式二:
		System.out.println(l3.indexOf("b"));// 1
		System.out.println(l3.lastIndexOf("b"));// 3

		// 操作六:
		// 删除数据 【根据对象删除、根据索引删除】
		// 根据对象删除
		l3.remove("a");
		System.out.println(l3);// [b, c, b]

		// 根据索引删除
//		l3.remove(2);
//		System.out.println(l3);//[a, b, b]

		// 添加一个包装类的数据 然后删除该包装类
		l3.add(1);
		System.out.println(l3);// [b, c, b, 1]
		l3.remove(new Integer(1));
		System.out.println(l3);// [b, c, b]

		// 删除该集合中的全部数据
		l3.removeAll(l3);//根据集合的内容来进行删除 
		System.out.println(l3);//[]

		// 操作七:
		List l4 = new ArrayList();
		// 添加数据
		l4.add("a");
		l4.add("b");
		l4.add("c");
		l4.add("b");
		
		//替换数据
		l4.set(1,"B");
		System.out.println(l4);//[a, B, c, b]

		// 截取集合数据[开始,结束]
		List sublist = l4.subList(2, 4);
		System.out.println(sublist);// [c, b]
		
		// 以下后面会涉及到
		// 替换所有值
//		l4.replaceAll(operator);// lambda表达式

		// 比较器进行排序
//		list.sort(c);

		// 使用stream形式来遍历list集合
//		l4.stream.map();

//		//--判断是否包含某个元素
//		System.out.println(list.contains(2));
//		
//		//--判断集合是否为空
//		System.out.println(list.isEmpty());
//		
//		//--转换为数组
//		System.out.println(Arrays.toString(list.toArray()));
	}
}

为什么不推荐在增强foreach循环时使用元素的方法删除或增加元素的方法?

在增强for循环中,集合遍历是通过Iterator进行的,但是元素的add和remove却是直接使用的集合类自己的方法。这就导致了Iterator在进行下一次遍历时(调用next()方法),会发现有一个元素在自己不知道的情况下被删除/添加了(Iterator认为可能是其他线程操作的),就会抛出一个异常,用来提示用户,可能发生了并发修改。

解决方法:

  • 直接使用普通for循环进行操作
  • 直接使用Iterator提供的方法进行操作
  • 在删除要删除的第一个元素时立即结束循环

f9f8eda0867440858b4824d188b887f7.png

    // 注意:关于Iterator的源码在那里呢?打开的教程如下qwq,和相应的继承链
        // 在ArraysList中找以下源码
        //        public class ArrayList<E> extends AbstractList<E>
        //        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
        // 可以看到ArrayList<E>是继承于AbstractList<E>
        // 实现接口是 List<E>
        // 然后再打开List<E>的源码

        // 查看以下源码可以看出List<E>继承的
        // public interface List<E> extends Collection<E>

        // 然后再打开Collection<E>的源码
        // 可以看到Collection<E>是继承于Iterable<E>的
        // public interface Collection<E> extends Iterable<E>
        // 然后我们打开Iterable<E> 看到下面的源码就是最大的接口到这里已经无法打开了
        // public interface Iterable<T>

        // Collection<E>中的源码往下滑既可以发现我们要用的迭代器啦qwq
        // Iterator<E> iterator();

-- List遍历代码

/**
 * @author Lantzrung
 * @date 2022年7月25日
 * @Description
 */
package com.day0725;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

public class ListDemo2 {
	public static void main(String[] args) {
		List list = new ArrayList();
		// 添加数据
		list.add("a");
		list.add("b");
		list.add("c");
		list.add("d");
		// 1、for【while】
		for (int i = 0; i < list.size(); i++) {
			System.out.println(list.get(i));
		}

		// 2、foreach
		for (Object object : list) {
			System.out.println(object);
		}

		// 3、使用迭代器进项迭代
		Iterator it = list.iterator();// 迭代器
		// 判断是否有下一个数据
		while (it.hasNext()) {
			// 获取下一个数据
			System.out.println(it.next());
		}

		System.out.println("-----------------");
		// 4、使用集合中的forEach方法 【labmda表达式】
		list.forEach(object -> System.out.println(object));

		System.out.println("----------------");

		// 5、遍历,当=="c"时删除
//		for (Object object : list) {
//			String str = (String) object;
//			if (str == "c") {
//				list.remove(object);
//			}
//		}
//		list.forEach(object -> System.out.println(object));
		
//		// 6、在集合再加入一个c进行操作,可能会出现并发异常
//		list.add("c");
//		for (Object object : list) {
//			String str = (String) object;
//			if (str.equals("c")) {
//				list.remove(object);  //出现并发异常
				Exception in thread "main" java.util.ConcurrentModificationException
				at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
				at java.util.ArrayList$Itr.next(ArrayList.java:851)
				at com.day0725.ListDemo2.main(ListDemo2.java:76)
//			}
//		}
//		System.out.println(list);
		
		// 7、为了避免会出现并发异常可以使用迭代器进行删除
		// 加入一个"c"元素
		list.add("c");
		System.out.println("删除前:"+list);//删除前:[a, b, c, d, c]
		Iterator iterator = list.iterator();
		// 判断是否有下一个数据
		while (iterator.hasNext()) {
			Object object = (Object) iterator.next();
			//
			if (object.equals("c")) {
				list.remove(object);//而且list是不可改变的,还是会出现并发异常,所以要是使用迭代器来删除元素Exception in thread "main" java.util.ConcurrentModificationException
//				iterator.remove(); //使用迭代器删除元素
			}
		}
		System.out.println("删除后:"+list);//删除后:[a, b, d]
	}
}

 

--ArrayList的继承链和实现接口

0234d8180d3b4be3b66db31e8814abd5.png

 

/**
 * @author Lantzrung
 * @date 2022年7月27日
 * @Description
 */
package day0726;

public class Inheritance_Chain {
	public static void main(String[] args) {
		// 1、首先我们先打开ArrayList的源码 往上滑找到
		// 这里可以看出ArrayList<E>是继承AbstractList<E>,然后实现于List<E>, RandomAccess, Cloneable, java.io.Serializable
//		public class ArrayList<E> extends AbstractList<E>
//        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
		
		// 2、然后我们再打开AbstractList<E> 发现
		// AbstractList<E>是个抽象类,然后继承于AbstractCollection<E>,接口实现于 List<E>
//		public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
         
		// 3、然后再打开AbstractCollection<E>发现
		// AbstractCollection<E>是个抽象类,接口实现于Collection<E>
//		public abstract class AbstractCollection<E> implements Collection<E> 
		
		// 3.1、当然这里我们可以分支打开List<E> 发现
		// List<E>接口继承于Collection<E>
		// public interface List<E> extends Collection<E> 

		// 4、然后我们打开Collection<E> 发现Collection<E>接口继承于 Iterable<E>
		// public interface Collection<E> extends Iterable<E>
		
		// 5、最后我们打开Iterable<E> 发现它是最大的接口
		// public interface Iterable<T>
	}
}

 

--ArrayList的工作原理


public class Working_Principle {
	public static void main(String[] args) {
// ArrayList()的工作原理:
		// 1、先打开ArrayList的源码
//		public ArrayList() {
//		        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
//		}
		// 2、然后再打开elementData看到它是又一个Object[]的数组创建的引用变量
		// transient Object[] elementData; // non-private to simplify nested class
		// access

		// 3、打开DEFAULTCAPACITY_EMPTY_ELEMENTDATA是一个空的数组
		// 看到private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

		// 4、然后我们在这里面ctrl+f查找add可以找到添加的方法
		// 然后我们在里面看到有一个ensureCapacityInternal的方法打开
//		  public boolean add(E e) {
                 //9、首先这里先判断它长度够不够,要是不够就扩容 ,如果长度够了就进行计数
//		        ensureCapacityInternal(size + 1);  // Increments modCount!!
//		        elementData[size++] = e;
//		        return true;
//		    }
		// 5、创建时默认为空数组,当第一次添加数据时会初始化长度为10
        // 这是是判断elementData是否为空 也是ArrayList默认初始化为10的方法过程
//	    private void ensureCapacityInternal(int minCapacity) {
//	        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//	            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
//	        }
//
//	        ensureExplicitCapacity(minCapacity);// 默认初始化
//	    }
		// 6、我们先打开
		// private static final int DEFAULT_CAPACITY = 10;
		// 如果为空那么DEFAULT_CAPACITY默认初始化为10 minCapacity最小值返回10

		// 7、然后打开ensureExplicitCapacity,可以看到以下方法
		// minCapacity的默认值为10 然后ensureExplicitCapacity是用来扩展的modCount++;
		// 然后if判断10-0>0所以返回grow(minCapacity)
//		private void ensureExplicitCapacity(int minCapacity) {
//	        modCount++;//10、这里的一个计数单位,它是用来存放有去做了多少个数据的保存
//
//	        // overflow-conscious code
//	        if (minCapacity - elementData.length > 0)
//	            grow(minCapacity);
//	    }

		// 8、然后再打开grow源码查看
        // 如果后面插入数据时,长度不够则会进行1.5倍的扩容【使用数组拷贝实现】
//		 private void grow(int minCapacity) {
//		        // overflow-conscious code
//		        int oldCapacity = elementData.length;
//		        int newCapacity = oldCapacity + (oldCapacity >> 1);
		// 然后 1 + 0.5 这里的右移相当于除以2
		// 新的长度等于旧的长度 = 1.5倍
//		        if (newCapacity - minCapacity < 0)
//		            newCapacity = minCapacity;
//		        if (newCapacity - MAX_ARRAY_SIZE > 0)
//		            newCapacity = hugeCapacity(minCapacity);
//		        // minCapacity is usually close to size, so this is a win:
//		        elementData = Arrays.copyOf(elementData, newCapacity); //这里是指1.5倍的 
//              扩容
                 
		// 11、在ArrayList中查看size源码
//        public int size() {
//            return size;
//    }
//		   private int size;//可以返回当前size的长度
  }
}

 // 总结: ArrayList本身就是一个数组 【默认初始化长度为10】 【每次扩展因子为1.5倍】 通过【Arrays.copyOf()的方法来进行扩容的】

--创建时默认为空数组,当第一次添加数据时会初始化长度为10
--如果后面插入数据时,长度不够则会进行1.5倍的扩容【使用数组拷贝实现】

        //  1、 private void ensureCapacityInternal(int minCapacity) {
        //        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        //        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        //    }
        //  来使得ArrayList【默认初始化长度为10】
        //  2、int newCapacity = oldCapacity + (oldCapacity >> 1);是为扩展因子
        //  3、Arrays.copyOf()是扩容方法 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lantzruk

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值