1.为什么使用集合
开发过程中会使用大量相同数据类型 如果使用数组来解决问题
1.数组能够使用的方法非常少,功能方法徐涛程序员自己完成
2.数据类型单一化,不支持对中情况
3.数组容量不可以更改
集合为解决问题而生
1.方法多种多样,基本都能完善
2.数组支持多样化,单又不失数据类型一致要求
3.容量可以变,并且不用开发者操心
1.2集合架构
java中集合的【总接口】Collection<E> java中所有的集合有关的内容,都是Collection<E>接口的子接口或者实现类
interface Collection<E>
--interface List<E>List接口,有序可重复
1.class ArrayList<E>
【重点】可变长数组结构
源码实现,了解区中的特征,性能。。
2.class LinkedList<E>
【重点】双向链表结构
3. class Vector<E>
--interface Set<E> Set接口,无序不可重复
1.HashSet<E> 底层存储数据的方式是采用哈希表方式
2.TreeSet<E> 底层存储数据的方式一个平衡二叉树方式
1.3Collection<E> 接口下的常用方法
增:
boolean add(E e); 存入元素到当前集合对象中,这里要求的数据类型是E类型,也就是泛型对于的具体数据类型
bollean addAll(Collection<? extends E> c); ==?extends E 泛型的上限 要求存入的集合c中,存储的元素要么是E类型,要么是E类型的子类
删:
void clear();清空整个集合
boolean remove(Object obj);删除集合中的指定元素
boolean retainAll(Collection<?> c);保留两个集合的交集
改:
查:
int size();返回集合中有效元素个数
boolean isEmpty();判断当前集合是否为空
boolean contains(Object obj);判断指定元素在当前集合中是否存在
boolean containsAll(Collection<?> c);判断集合c是不是存在当前集合的子集合
package Collection;
import java.util.ArrayList;
import java.util.Collection;
public class Demo1 {
public static void main(String[] args) {
/*
* 因为Collection<E>使用一个接口,接口没有自己的类对象
* 这里使用Collection接口的实现类来完成演示过程 ArrayList<E>
*/
Collection<String> c = new ArrayList<String>();
c.add("82年的拉菲");
c.add("82年的雪碧");
c.add("82年的可乐");
c.add("82年的老雪");
System.out.println(c);
Collection<String> c1 = new ArrayList<String>();
c1.add("百威");
c1.add("福佳白");
c1.add("精酿啤酒");
c1.add("修道院啤酒");
c.addAll(c1);
System.out.println(c);
c.remove("82年的雪碧");
System.out.println(c);
//c.removeAll(c1);
//System.out.println(c);
//c.retainAll(c1);
//System.out.println(c);
System.out.println("size:" + c.size());
System.out.println(c.isEmpty());
System.out.println(c.contains("百威"));
System.out.println(c.contains("哈尔滨"));
System.out.println(c.containsAll(c1));
c1.add("野格");
System.out.println(c.containsAll(c1));
}
}
1.4迭代器
通过集合对象获取对应的Iterator<E>迭代器
Iterator<E> iterator();
常用方法:
boolean hasNext();判断当前迭代器是否可以继续运行
E next();获取Iterator当前指向元素,并且指向下一个元素
void remove() 删除
【注意】:
1.remove 方法有且只能通过next方法获取的元素
2.remove方法如果想要使用,必须紧挨着next方法
package com.qfedu.a_collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Demo2 {
public static void main(String[] args) {
ArrayList<String> c = new ArrayList<String>();
c.add("烤羊排");
c.add("油焖大虾");
c.add("土豆牛肉");
c.add("黄焖鸡米饭");
c.add("麻辣香锅");
c.add("孜然肉片");
c.add("酸汤肥牛");
Iterator<String> iterator = c.iterator();
/*
* ConcurrentModificationException
* Iterator在创建的过程中,会对整个集合所有元素打招呼,记录每一个元素位置。
* Iterator在执行next方法过程中,会按照初始条件一个一个遍历
* 当前集合通过remove方法,删除已经被Iterator记录的元素时,是有可能导致
* Iterator一脸懵逼!!!元素不见了!!!
*
* 这里就会发生冲突!
*
* 这里因为集合中元素,对于集合本身和当前Iterator而言是一个共享资源
* 不管是哪一方操作元素,都存在影响对方操作的情况。【共享资源冲突问题】
*
* ArrayList存储元素不是连续的吗,土豆牛肉删除了,他的位置不是会被后面的元素顶上来吗
*/
while (iterator.hasNext()) {
System.out.println(iterator.next());
// 这里通过集合删除土豆牛肉元素
// 后期代码中会出现很多相同名字方法,这里一定要注意!!!
// 调用当前方法的是哪一个
c.remove("酸汤肥牛");
}
}
}
Iterator出现并发错误原因
2.List
2.1List集合接口特征和方法
特征:有序,可重复
有序:添加顺序和存储顺序一致
可重复: 相同元素可以同时添加
List<e>接口下的实现类,存在一定的下标操作机制
ArrayList<E>底层数组形式操作,可以通过下标直接访问
LinkedList<E> 底层是一个双向链表结构,下标 ==> 计数器
特定的方法:
增:
add(E e) ;
List接口下,当前方法时添加元素到集合的末尾,尾插法
addAll(Collection<? extends E> c);
List接口下,当前方法是添加另一个集合到当前集合的末尾 ,要去添加的集合中保存的元素和当前集合保存元素一致,或者说是当前集合保存元素的子类
add(int index,E e);
在指定的下标位置,添加指定元素
addAll(int index,Collection<? extends E> c);
在指定的下标位置,添加 指定的集合,集合要求同上一个addAll方法
删:
void clear();
清空整个集合;
remove(Object obj);
删除集合中的指定元素
removeAll(Collection<?> c);
删除两个集合的交集
retainAll(Collection<?> c);
保留两个集合的交集
改:
E set(int index,E e);
使用指定元素替代 指定下标index的元素,返回值是被替换掉的元素。
查:
int size();
有效元素个数
boolean isEmpty();
判断当前集合是否为空
boolean contains(Object obj);
boolean containsAll(Collection<?> c);
int indexOf(Object obj);
找出指定元素在集合中的第一次出现位置
int lastIndexOf(Object obj);
找出指定元素在集合中 最后一次出现位置
E get(int index);
找出指定下标元素
Last<E> subList(int fromIndex,int endIndex);
获取当前集合的子集合
【特征】
获取数据的范围是 fromIndex <= n< endIndex
要头不要尾
2.2 ArrayList 可变长数组
特征:
数组形式的操作方式,查询效率高,但是删除 ,增加效率低
数组:
Object类型数组
方法:
ArrayList使用的方法基本上都是从List接口中遵从实现的方法。
特征:
ensureCapacity(int minCapacity);判断当前容量是否足够
timeToSize();
截断整个数组容量 ==>size有效元素个数
时间换空间,空间换时间
自定义实现的ArrayList
Constructor 构造方法
add(E e);
add(int index, E e);
addAll(自定义ArrayList<E> e)
addAll(int index,自定义ArrayList<E> e)
remove(Object obj);
remove(int index);
set(int index, E);
E get(int index);
int indexOf();
int lastIndexOf();
boolean contains(Object obj);
boolean containsAll(自定义ArrayList类型 list)
boolean isEmpty();
int size();
自定义ArrayList subList(int fromIndex, int endIndex);
Object[] toArray();
package com.qfedu.c_util;
import java.util.Arrays;
/**
* 自定义实现MyArraylist
* @author Anonymous
*
* @param <E> 自定义泛型
*/
public class MyArrayList<E> {
/**
* 准备一个底层数组,用于存储数据内容
*/
private Object[] elements;
/**
* 初始化默认容量
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 最大数组容量, -8是为了腾出一定的空间,保存数组的必要内容
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* 当前底层数组中保存的有效元素个数
*/
private int size = 0;
/**
* 无参数构造方法,但是需要提供给用户一个初始化容量来保存必要的数据
*/
public MyArrayList() {
elements = new Object[DEFAULT_CAPACITY];
}
/**
* 用户指定保存元素容量的初始化过程,要求用户指定的容量范围是有效的
*
* @param initCapacity 用户指定的初始化容量,但是不能小于等于0 ,不能大于
* MAX_ARRAY_SIZE
*/
public MyArrayList(int initCapacity) {
// 用户传入参数的合法性判断过程
if (initCapacity < 0 || initCapacity > MAX_ARRAY_SIZE) {
// 抛出异常
// IllegalArgumentException 是一个RuntimeException运行时异常的子类
// 不需要强制声明抛出异常
throw new IllegalArgumentException("IllegalArgumentException : " + initCapacity);
}
elements = new Object[initCapacity];
}
/*
* 增加方法
*/
/**
* 添加元素到当前集合的末尾
*
* @param e 要求是符合泛型约束的指定数据类型
* @return 添加成功返回true, 否则返回false
*/
public boolean add(E e) {
// 直接调用在指定下标位置添加元素的方法,只不过这里指定下标位置就是
// 尾插法下标位置
return add(size, e);
}
/**
* 在底层数组的指定下标位置保存对应的元素
*
* @param index 指定下标位置,不能超出有效范围,0<= index <= size
* @param e 符合泛型约束的数据类型
* @return 添加成功返回true, 否则返回false
*/
public boolean add(int index, E e) {
if (index < 0 || index > size) {
throw new ArrayIndexOutOfBoundsException(index);
}
ensureCapacity(size + 1);
for (int i = size; i > index; i--) {
elements[i] = elements[i - 1];
}
elements[index] = e;
size += 1;
return true;
}
/*
* addAll方法
* 1. 需要得到添加集合中元素内容,有效元素个数
* 2. 确认容量问题
* 3. size = srcSize + newSize
*/
/**
* 添加另一个集合到当前集合的末尾
*
* @param list MyArrayList类型,自定义ArrayList,要求存储元素和当前集合一致,或者
* 是其子类
* @return 添加成功返回true,添加失败返回false
*/
public boolean addAll(MyArrayList<? extends E> list) {
Object[] array = list.toArray();
int newSize = array.length;
ensureCapacity(size + newSize);
for (int i = 0; i < newSize; i++) {
elements[i + size] = array[i];
}
size += newSize;
return true;
}
/**
* Do yourself 作业
* @param index
* @param list
* @return
*/
public boolean addAll(int index, MyArrayList<? extends E> list) {
return true;
}
/**
* 删除指定元素
*
* @param obj 指定删除的元素
* @return 删除成功返回true
*/
public boolean remove(Object obj) {
int index = indexOf(obj);
return null != remove(index);
}
/**
* 删除下标元素
*
* @param index 指定的下标范围
* @return 删除成功返回对应元素,失败返回null
*/
public E remove(int index) {
if (-1 == index) {
return null;
}
E e = get(index);
for (int i = index; i < size - 1; i++) {
elements[i] = elements[i + 1];
}
// 原本最后一个有效元素位置上的内容赋值为null
elements[size - 1] = null;
size -= 1;
return e;
}
/**
* 获取集合中指定下标的元素
*
* @param index 指定下标的范围,但是不能超出有效下标范围
* @return 返回对应的元素
*/
@SuppressWarnings("unchecked")
public E get(int index) {
if (index < 0 || index > size) {
throw new ArrayIndexOutOfBoundsException(index);
}
return (E) elements[index];
}
/**
* 查询指定元素在集合中的第一次出现下标位置
* @param obj 指定的元素
* @return 返回值大于等于0表示找到元素,否则返回-1
*/
public int indexOf(Object obj) {
int index = -1;
for (int i = 0; i < size; i++) {
// equals 判断对象是否一致地方的方法
if (obj.equals(elements[i])) {
index = i;
break;
}
}
return index;
}
/**
* 查询指定元素在集合中的最后一次出现下标位置
*
* @param obj 指定的元素
* @return 返回值大于等于0表示找到元素,否则返回-1
*/
public int lastIndexOf(Object obj) {
int index = -1;
for (int i = size - 1; i >= 0; i--) {
// equals 判断对象是否一致地方的方法
if (obj.equals(elements[i])) {
index = i;
break;
}
}
return index;
}
/**
* 返回MyArrayList集合中所有有效元素的Object数组
*
* @return 包含所有集合元素的Object类型数组
*/
public Object[] toArray() {
// size是有效元素个数,通过该方法可以获取到一个只有当前数组中有效元素的数组
return Arrays.copyOf(elements, size);
}
/**
* 替换指定下标的元素
*
* @param index 指定下标元素,但是必须在有效范围以内
* @param e 符合泛型约束的对应数据类型
* @return 被替换的元素
*/
public E set(int index ,E e) {
if (index < 0 || index >= size) {
throw new ArrayIndexOutOfBoundsException(index);
}
E temp = get(index);
elements[index] = e;
return temp;
}
/**
* 判断指定元素是否存在
*
* @param obj 指定元素
* @return 存在返回true,不存在返回false
*/
public boolean contains(Object obj) {
return indexOf(obj) > -1;
}
/**
* 作业
* 集合1 {1, 3, 5, 7, 9}
* 集合2 {3, 5, 7} 子集合!!True
* 集合3 {3, 7, 5} 不是!!! false
*
* @param list
* @return
*/
public boolean containsAll(MyArrayList<?> list) {
return false;
}
/**
* 判断集合是否是空的
*
* @return 如果为空,返回true, 否则返回false
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 获取当前集合中有效元素个数
*
* @return 有效元素个数
*/
public int size() {
return size;
}
/**
* 获取当前集合的子集合,截取范围是fromIndex <= n < endIndex
*
* @param fromIndex fromIndex <= endIndex 不得小于0
* @param endIndex endIndex >= fromIndex 小于等于size
* @return 截取得到的一个MyArrayList子集合对象
*/
public MyArrayList<E> subList(int fromIndex, int endIndex) {
if (fromIndex > endIndex || fromIndex < 0 || endIndex > size) {
throw new ArrayIndexOutOfBoundsException();
}
MyArrayList<E> listTemp = new MyArrayList<E>(endIndex - fromIndex);
for (int i = fromIndex; i < endIndex; i++) {
listTemp.add(this.get(i));
}
return listTemp;
}
@Override
public String toString() {
String str = "[";
for (int i = 0; i < size - 1; i++) {
str += elements[i] + ", ";
}
return str + elements[size - 1] + "]";
}
/*
* 这里需要类内使用的可以用于判定当前容量是否满足添加要求的方法
* 如果满足直接进入添加模式,如果不满足,需要执行grow方法,完成
* 底层数组的扩容问题。
*/
/**
* 每一次添加元素,都需要进行容量判断,如果满足可以进行添加操作
* 不满足需要制定grow方法
*
* @param minCapacity 要求的最小容量
*/
private void ensureCapacity(int minCapacity) {
if (minCapacity > elements.length) {
// 完成一个底层数组的扩容方法
grow(minCapacity);
}
}
/**
* 底层数组的扩容方法,原理是创建新数组,移植数据,保存新数组地址
*
* @param minCapacity 要求的最小容量
*/
private void grow(int minCapacity) {
// 1. 获取原数组容量
int oldCapacity = elements.length;
// 2. 计算得到新数组容量
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 3. 判断新数组容量是否满足要求
if (newCapacity < minCapacity) {
newCapacity = minCapacity;
}
// 新数组容量是大于允许的最大数组容量
if (newCapacity > MAX_ARRAY_SIZE) {
// 二次判断minCapacity是否小于MAX_ARRAY_SIZE
if (minCapacity < MAX_ARRAY_SIZE) {
// 最小要求是不大于MAX_ARRAY_SIZE,代码可以运行
newCapacity = minCapacity;
} else {
throw new OutOfMemoryError("Overflow MAX_ARRAY_SIZE");
}
}
/*
* 4. 使用数组工具类方法完成操作
* Arrays.copyOf(源数据数组,可以是任意类型,采用泛型约束, 指定的新数组容量);
* a. 根据指定的新数组容量创建对应泛型数据类型的新数组
* b. 从源数据数组中拷贝内容到新数组中
* c. 返回新数组首地址
*/
elements = Arrays.copyOf(elements, newCapacity);
}
}
test代码
package com.qfedu.d_test;
import com.qfedu.c_util.MyArrayList;
public class Demo1 {
public static void main(String[] args) {
MyArrayList<String> list = new MyArrayList<String>();
list.add("郭德纲");
list.add("于谦");
list.add("高峰");
list.add("栾云平");
MyArrayList<String> list1 = new MyArrayList<String>();
list1.add("岳云鹏");
list1.add("孙越");
list1.add(0, "孟鹤堂");
list1.add("周九良");
System.out.println(list);
System.out.println(list1);
list.addAll(list1);
list.addAll(list1);
System.out.println(list);
list.remove(3);
System.out.println(list);
list.remove("周九良");
System.out.println(list);
System.out.println(list.contains("麻花藤"));
System.out.println(list.get(0));
System.out.println(list.size());
System.out.println(list.indexOf("孟鹤堂"));
System.out.println(list.lastIndexOf("孟鹤堂"));
MyArrayList<String> subList = list.subList(0, 5);
System.out.println(subList);
System.out.println(list.set(5, "烧饼"));
System.out.println(list);
}
}