栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

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

--数组-线性表

--链表

--栈

--队列

--散列表

--二叉树

--映射关系(key-value)

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

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

--List集合的使用

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 extends Collection {
//		    // Query Operations
//		    
//		}

//	 	Collection c = new ArrayList();

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

		// 定义一个容器可以存放多个数据,并且数据是可变的【集合】【功能方便、强大】
		// 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提供的方法进行操作
  • 在删除要删除的第一个元素时立即结束循环

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

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

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

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

-- List遍历代码

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的继承链和实现接口

package day0726;

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

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

--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()是扩容方法 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1027491.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号