Collection集合

集合

1. 数组的弊端

1. 数据类型一旦确定,只能存储对应类型数据【类型单一】
2. 容量无法修改
3. 配套方法少

集合!!!
	1. 集合支持数据类型多样性,同时满足数据类型一致化 【泛型】
	2. 集合数据存储容量不需要程序员关注,会自动的扩容或者调整
	3. 方法众多!!!各种辅助工具,操作方便!!!【背方法】

2. Java 中集合的整体结构

interface Collection<E> Java中所有集合的总接口!!!
--| interface List<E> extends Collection<E>
	List 接口,对应集合特征: 有序,可重复,存在【下标】操作
	--| class ArrayList<E> implements List<E>
    	可变长数组结构,也是开发中使用较多的一种结构
	--| class LinkedList<E> implements List<E>
    	双向链表结构【火车】
	--| class Vector<E> implements List<E>  
		可变长数组结构,线程安全结构,效率低,是ArrayList的老版
--| interface Set<E> extends Collection<E> 
	Set 接口,对应集合特征: 无序,不可重复
	--| class HashSet<E> implements Set<E>
		底层数据存储结构为【哈希表 Excel】结构
	--| class TreeSet<E> implements Set<E>	
		底层数据存储结构为【平衡二叉树】结构

3. Collection 接口常用方法

增
	add(E e);
		添加在实例化对象过程中约束泛型对应具体数据类型元素对象。
	addAll(Collection<? extends E> c);
		添加参数集合到当前调用方法集合中,要求参数集合对象存储数据类型必须是当前集合对象实例化过程中,约束泛
		型对应具体数据类型,或者其他子类类型。
删
	remove(Object obj);
		在当前集合中,删除指定元素
    removeAll(Collection<?> c);
    	在当前集合中,删除参数集合和当前集合的交集
    retainAll(Collection<?> c);
    	在当前集合中,仅保留参数集合和当前集合的交集
    clear();
    	清空当前集合中所有数据内容
查
	int size();
		当前集合中有效元素个数
	boolean isEmpty();
		判断当前集合是否为空
	boolean contains(Object obj);
		判断参数对象是否在当前集合中存在
	boolean containsAll(Collection<?> c);
		判断参数集合是否是当前集合的子集合
	Object[] toArray();
		返回当前集合中所有元素对象的Object类型数组
	

4. 补充知识点,泛型上限

addAll(Collection<? extends E> c);
class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}
class Pig extends Animal {}

Collection<Animal>
	Collection<? extends Animal> c
	限制泛型的数据类型上限,要求集合中存储数据类型必须是 Animal类型或者其子类类型
	? 泛型通配符

Collection<?> c
	对于当前集合数据存储类型不限制

5. List集合

5.1 List 集合特征演示
有序
	添加顺序和存储顺序一致,采用的存储方式为【尾插法】
可重复
	元素允许出现重复情况!!!

List集合可以通过【下标/索引】方式操作使用!!!
5.2 List 常用方法
增
	add(E e);
		添加在实例化对象过程中约束泛型对应具体数据类型元素对象。
	addAll(Collection<? extends E> c);
		添加参数集合到当前调用方法集合中,要求参数集合对象存储数据类型必须是当前集合对象实例化过程中,约束泛
		型对应具体数据类型,或者其他子类类型。
	add(int index, E e);
		在指定下标位置,添加在实例化对象过程中约束泛型对应具体数据类型元素对象。
	addAll(int index, Collection<? extends E> c);
		在指定下标位置,添加参数集合到当前调用方法集合中,要求参数集合对象存储数据类型必须是当前集合对象实例化
		过程中,约束泛型对应具体数据类型,或者其他子类类型。
删
	E remove(int index);
		在当前List集合中,删除指定下标元素,返回值是被删除元素对象本身		
	remove(Object obj);
		在当前集合中,删除指定元素
    removeAll(Collection<?> c);
    	在当前集合中,删除参数集合和当前集合的交集
    retainAll(Collection<?> c);
    	在当前集合中,仅保留参数集合和当前集合的交集
    clear();
    	清空当前集合中所有数据内容
改
	E set(int index, E e);
		在 List 集合中,使用符合实例化对象过程中约束泛型对应具体数据类型对象,替换指定下标元素,返回值是被替换
		元素对象本身
查
	int size();
		当前集合中有效元素个数
	boolean isEmpty();
		判断当前集合是否为空
	boolean contains(Object obj);
		判断参数对象是否在当前集合中存在
	boolean containsAll(Collection<?> c);
		判断参数集合是否是当前集合的子集合
	Object[] toArray();
		返回当前集合中所有元素对象的Object类型数组
	E get(int index);
		在当前集合中,获取指定下标元素
	int indexOf(Object obj);
		获取指定元素在当前集合中第一次出现的下标位置
	int lastIndexOf(Object obj);
		获取指定元素在当前集合中最后一次出现的下标位置
	List<E> subList(int fromIndex, int toIndex);
		从 fromIndex 下标开始,到 toIndex 下标结束,获取子集合对象,要求要头不要尾
5.3 ArrayList 可变长数组集合
ArrayList集合底层是一个 Object 类型数组,在使用过程中,可以自行扩容或者根据条件进行缩容操作。
使用 无参数构造方法创建 ArrayList 可变长集合对象,默认底层 Object 数组初始化容量为 10
  /**
   * Default initial capacity.
   */
  private static final int DEFAULT_CAPACITY = 10;
 
整个 ArrayList 集合允许的最大容量是 Integer 数据最大值 - 8
 /**
 * The maximum size of array to allocate.
 * Some VMs reserve some header words in an array.
 * Attempts to allocate larger arrays may result in
 * OutOfMemoryError: Requested array size exceeds VM limit
 */
 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

可变长功能实现依赖于核心方法 grow(int minCapacity)
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    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);
}
增删慢
查询快
5.4 增删慢
增:
	1. 尾插法
	2. 指定下标位置添加
	增加影响效率的情况
		1. 底层数组扩容导致时间浪费和空间浪费。
			空间浪费在于新数组容量占用空间和原数组容量占用空间
			时间浪费在新数组创建,原数组内存销毁,数据移动复制操作
		2. 指定下标位置数据添加,从添加位置开始,数组中的每一个元素整体向后移动,

删:
	1. 删除指定下标元素,或者删除指定元素
	2. 删除操作之后,数组中有效元素个数和数组容量比例超过一定范围。【缩容】
	删除影响效率的情况
		1. 删除元素之后,从删除位置开始,数组中元素整体向前移动,时间效率较低
		2. 触发缩容操作,数组中的内存会释放,内存需要时间来销毁。【trimToSize】
5.5 补充知识点 内存地址问题
什么是内存地址???
	计算机按照最小数据单元(byte 字节),对内存进行编号!!!编号从 0 开始到内存的最大值
	以 4GB 内存为例地址范围
		0 ~ 4294967295 为了操作方便,内存按照 十六进制数据处理
		0x0 ~ 0xFFFF FFFF
	编号之后,CPU 可以通过内存地址直达内存目标,读取对应数据,执行相关内容,效率极高【寻址】。

null ==> 0x0 内存编号为 0 的内存。
	0x0 受到系统保护,任何程序,任何代码不允许读取或者写入数据到 0x0 中,一旦操作。操作系统发送 kill 命令
	Java中的 NullPointerException 就是因为程序操作使用了 0x0 内存导致报错!!!
5.6 数组内存分析图

5.7 查询快
	ArrayList 底层是一个 Object 数组结构,数据存储空间连续,同时有下标/索引操作。ArrayList 完成可以按照数组+下标方式计算对应元素的内存空间地址,直接操作对应元素内容。
5.8 ArrayList 原码实现
增
	add(E e);
		添加在实例化对象过程中约束泛型对应具体数据类型元素对象。
	add(int index, E e);
		在指定下标位置,添加在实例化对象过程中约束泛型对应具体数据类型元素对象。
删
	E remove(int index);
		在当前List集合中,删除指定下标元素,返回值是被删除元素对象本身		
	remove(Object obj);
		在当前集合中,删除指定元素
    clear();
    	清空当前集合中所有数据内容
改
	E set(int index, E e);
		在 List 集合中,使用符合实例化对象过程中约束泛型对应具体数据类型对象,替换指定下标元素,返回值是被替换
		元素对象本身
查
	int size();
		当前集合中有效元素个数
	boolean isEmpty();
		判断当前集合是否为空
	boolean contains(Object obj);
		判断参数对象是否在当前集合中存在
	Object[] toArray();
		返回当前集合中所有元素对象的Object类型数组
	E get(int index);
		在当前集合中,获取指定下标元素
	int indexOf(Object obj);
		获取指定元素在当前集合中第一次出现的下标位置
	int lastIndexOf(Object obj);
		获取指定元素在当前集合中最后一次出现的下标位置
	MyArrayList<E> subList(int fromIndex, int toIndex);
		从 fromIndex 下标开始,到 toIndex 下标结束,获取子集合对象,要求要头不要尾
package com.qfedu.b_list;

import java.util.Arrays;

/**
 * MyArrayList 可变长数组结构原码实现
 * @author Anonymous
 *
 * @param <E> 自定义泛型占位符
 */
public class MyArrayList<E> {
	/**
	 * 底层 Object 数组,用于存储元素
	 */
	private Object[] elementData;
	
	/**
	 * 默认 MyArrayList 可变长数组容量 为 10
	 */
	private static final int DEFAULT_CAPACITY = 10;
	
	/**
	 * 允许的数组最大容量
	 */
	private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
	
	/**
	 * 记录有效元素个数
	 */
	private int size;
	
	/**
	 * 无参数构造方法
	 */
	public MyArrayList() {
		/*
		 * 在构造方法中,this(实际参数)。会根据实际参数的数据类型,参数个数,参数顺序
		 * 选择调用类内的其他构造方法。
		 * 
		 * Constructor call must be the first statement in a constructor
		 * 在一个构造方法中,使用 this(实际参数) 调用其他构造方法,必须是整个代码块的第一行!!!
		 */
		this(DEFAULT_CAPACITY);
	}
	
	/**
	 * 提供给用户明确 MyArrayList 底层数组初始化容量构造方法
	 * 
	 * @param initCapacity 用户指定的底层数组初始化容量
	 */
	public MyArrayList(int initCapacity) {
		if (initCapacity < 0 || initCapacity > MAX_ARRAY_SIZE) {
			/*
			 * IllegalArgumentException 非法参数异常
			 */
			throw new IllegalArgumentException("非法参数异常");
		}
		
		elementData = new Object[initCapacity];
	}
	
	/**
	 * 尾插法添加实例化对象明确的泛型约束数据类型元素
	 * 
	 * @param e 泛型约束对应具体数据类型
	 * @return 操作成功返回true,否则返回false
	 */
	public boolean add(E e) {
		/*		
		if (size == elementData.length)
		ensureCapacity(size + 1);	
		elementData[size++] = e;
		return true;
		给用户多样性的选择,同时满足方法执行任务的一致性!!!
		*/
		return add(size, e);
	}
	
	/**
	 * 在指定下标位置添加符合实例化对象明确的泛型约束数据类型元素
	 * 
	 * @param index 用户指定添加元素的下标位置
	 * @param e     符合实例化对象明确的泛型约束数据类型元素
	 * @return 操作成功返回true,否则返回false
	 */
	public boolean add(int index, E e) {
		// add 添加方法指定下标是否合法校验
		addRangCheck(index);
	
		// add 添加操作容量是否足够校验
		ensureCapacity(size + 1);
			
		// 从最后一个有效下标位置开始移动数据
		for (int i = size; i > index; i--) {
			elementData[i] = elementData[i - 1];
		}
		
		elementData[index] = e;
		size += 1;
		
		return true;
	}

 	/**
 	 * 添加另一个MyArrayList集合对象到当前集合中,要求参数集合存储类型和当前集合存储类型一致
 	 * 
 	 * @param list MyArrayList 集合对象,存储元素必须和当前集合一致
 	 * @return 操作成功返回true,否则返回false
 	 */
	public boolean addAll(MyArrayList<E> list) {
		
		ensureCapacity(size + list.size());
		
		return true;
	}
	
	/**
	 * 在指定下标位置,添加另一个MyArrayList集合对象到当前集合中,要求参数集合存储类型和当前集合存储类型一致
	 * 
	 * @param index 指定添加操作者下标位置
	 * @param list  MyArrayList 集合对象,存储元素必须和当前集合一致
	 * @return  操作成功返回true,否则返回false
	 */
	public boolean addAll(int index, MyArrayList<E> list) {
		addRangCheck(index);

		ensureCapacity(size + list.size());
		
		return true;
	}
	
	/**
	 * 获取当前MyArrayList 集合中的有效元素个数
	 * 
	 * @return 有效元素个数
	 */
	public int size() {
		return size;
	}
	
	/**
	 * 判断当前添加操作集合当前容量是否足够,如果不足,调用 grow 方法进行扩容
	 * 
	 * @param minCapacity 是当前集合添加操作所需最小容量要求
	 */
	private void ensureCapacity(int minCapacity) {
		if (minCapacity > elementData.length) {
			grow(minCapacity);
		}
	}
	
	/**
	 * 底层数组扩容方法
	 * 
	 * @param minCapacity 扩容操作最小集合容量需求
	 */
	private void grow(int minCapacity) {
		int oldCapacity = elementData.length;
		int newCapacity = oldCapacity + oldCapacity / 2;
		
		if (newCapacity < minCapacity) {
			newCapacity = minCapacity;
		}
		
		if (newCapacity > MAX_ARRAY_SIZE) {
			throw new OutOfMemoryError("内存溢出错误");
		}
		
		/*
		Object[] newArray = new Object[newCapacity];
		
		for (int i = 0; i < oldCapacity; i++) {
			newArray[i] = elementData[i];
		}
		
		elementData = newArray;
		
		 * Arrays 数组工具类方法
		 * 
		 * public static <T> T[] Arrays.copyOf(T[] t, int newLength);
		 *    根据用户传入的数组,以及新数组容量,创建对应当前数组存储数据类型新数组,并且将数据从原数组
		 * 中复制到新数组中,返回新数组首地址
		 */
		elementData = Arrays.copyOf(elementData, newCapacity);
	}
	
	/**
	 * private 修饰的私有方法,用于校验添加方法指定下标位置是否合法
	 * 
	 * @param index 用户指定添加操作的下标位置
	 */
	private void addRangCheck(int index) {
		if (index < 0 || index > size) {
			throw new ArrayIndexOutOfBoundsException(index);
		}
	}
}
posted @ 2022-05-15 23:24  qtyanan  阅读(30)  评论(0编辑  收藏  举报