数组定义后类型确定,长度固定 集合类型可以不固定,大小是可变的
数组可以存储基本类型和引用类型的数据 集合只能存储引用类型的数据
数组适合做数据个数和类型确定的场景 集合适合做数据个数不确定,且要做增删元素的场景
List系列集合 添加的元素是有序、可重复、有索引 ArrayList、LinekdList:有序、可重复、有索引
HashSet:无序、不重复、无索引 LinkedHashSet:有序、不重复、无索引
集合都是支持泛型的,可以在编译阶段约束集合只能操作某种数据类型
注意 集合和泛型都只能支持引用数据类型,不支持基本数据类型,所以集合中存储的元素都认为是对象
如果集合中要存储基本类型,要使用包装类 里面存储的45 12 也已经自动装箱,不再是基本数据类型
先学习的原因 Collection 是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的
public boolean add(E e)把给定的对象添加到当前集合中
public void clear() 清空集合中所有的元素
public boolean remove (E e)把给定对象在当前集合中删除
public boolean contains(Object obj)判断当前集合中是否包含给定对象
public boolean isEmpty()判断当前集合是否为空
public Object[] toArray() 把集合中的元素,存储到数组中 我们一般定义一个Object[]的数组,因为集合中的元素可能有很多类型(见上图)
方法一:迭代器(Iterator),迭代器是集合的专用的遍历方式
Iterator<E> iterator() 返回集合中的迭代器对象,该迭代器对象默认指向当前集合的0索引
快捷键:list.iterator() ctrl+alt+v
boolean hasNext() 询问当前位置是否有元素存在,存在返回true,不存在返回false
E next() 获取当前位置的元素,并同时将迭代器对象移向下一个位置,注意防止取出越界
既可以遍历集合,也可以遍历数组 其内部原理相当于Iterator迭代器,
实现Iterable 接口的类才可用迭代器和增强for,Collection接口已经实现了Iterable接口
格式 for(元素数据类型 变量名 :数组或者Collection集合){
default void forEach(Consumer<? super T> action) 结合lambda遍历集合
栈内存中的movies存储的是集合的地址,集合存储地址指向电影对象,增强for遍历通过mo变量来根据地址找内容
数据进入栈模型的过程为:压/进栈 数据离开栈模型的过程为:弹/出栈
链表的元素是游离存储的,每个元素节点包含数据值和下一个元素的地址
节点的度 节点拥有的子树的个数,二叉树的度不大于2,叶子节点 度为0的节点,也称之为终端节点
高度 叶子节点的高度为1,叶子节点的父节点高度为2,以此类推,根节点的高度最高
平衡二叉树 任意节点的左右两个字树的高度差不超过1,任意节点的左右两个字数都是一颗平衡二叉树
红黑树 红黑树是一种自平衡的二叉查找树,它的平衡不是通过高度平衡的,而是通过”红黑规则”实现的
规则 每一个节点或是红色,或是黑色,根节点必须是黑色 如果一个节点没有子节点或者父节点,则该节点相应的指针属性为Nil ,这些Nil视为叶节点,叶节点是黑色的
如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)
对于每一个节点,从节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点
List集合特有方法(List支持索引,所以多了很多索引操作的独特api,其它Collection的功能List也都继承了)
void add(int index,E element) 在此集合中的指定位置插入指定的元素
E remove (int index) 删除指定索引处的元素,返回被删元素
E set (int index,E element) 修改指定索引处的元素,返回被修改元素
可以用Collection的三种+自己独有的for索引遍历,因为有索引
ArrayList底层是基于数组实现的:根据索引定位元素快,增删需要元素的移位操作
第一次创建集合并添加第一个元素的时候,在底层创建一个默认长度为10的数组,当长度不够时,会以1.5倍的数组长度扩容
底层数据结构是双链表(既能代表栈,也能代表队列),查询慢,首尾操作的速度是极快的,所以多了很多首尾操作的特有API
public void addFirst(E e) 在该列表开头插入指定的元素
public void addLast(E e) 将指定的元素追加到此列表的末尾
public E getFirst() 返回此列表的第一个元素
public E getLast() 返回此列表中的最后一个元素
public E removeFirst() 从此列表中删除并返回第一个元素
public E removeLast()从此列表中删除并返回最后一个元素
当我们从集合中找出某个元素并删除的时候可能出现一种并发修改异常问题,例如当我们要删除的元素有两个或多个时,系统会判定异常,因为会出现漏删的情况
那些遍历存在问题 迭代器遍历集合且直接用集合删除元素的时候出现(见下图1注释部分) 增强for循环遍历集合且直接用集合删除元素时出现(Lambda也是如此,底层也是增强for)(原因是在删除元素时后面的元素会补上,然后索引值又会+1,导致删除不完全)
哪种遍历且删除元素不出问题 迭代器遍历集合但是用迭代器自己的删除方法操作可以解决(见下图1)
也会漏掉元素,以前我们解决这个问题是 从后面开始删 或 加 i–
可以在编译阶段约束操作的数据类型,并进行检查 格式 <数据类型> 泛型只能支持引用数据类型
好处 统一数据类型 把运行期间的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为编译阶段类型就能确定下来
定义类的同时定义了泛型的类就是泛型类 泛型类的格式:修饰符 class 类名 <泛型变量>{ }
范例 public class MyArrayList<T>{ } 此处的T可以写成任意标识 如E T K V
泛型方法的格式:修饰符<泛型变量> 方法返回值 方法名称 (形参列表){ }
范例:public <T> void show(T t){ } 方法中可以使用泛型接受一切实际类型的参数,使方法更具备通用性
格式 修饰符 interface 接口名称 <泛型变量>{} public interface Data<E>{}
实现原理 实现类可以在实现接口的时候传入自己的操作数据类型,这样重写的方法都将是针对于该类型的操作
接口实现类(泛型是Teacher类(前提是有Teacher这个类),那么重写的方法都是针对于该类型的操作)
?可以在”使用泛型”的时候代表一切类型 E T K V是在定义泛型的时候使用的
? extends Car : ?必须是Car 或者其子类 泛型上限
? super Car: ?必须是Car 或者其父类 泛型下限(如下图,Dog不是Car的子类,所以出错)
HashSet 无序 不重复 无索引 LinkedHashSet 有序 不重复 无索引 TreeSet 排序 不重复 无索引
HashSet集合底层采取哈希表存储的数据 哈希表是一种对于增删改查数据性能都较好的结构
JDK8之前 底层是数组+链表 JDK8之后 数组+链表+红黑树
是JDK根据对象的地址,按照某种规则算出来的int类型的数值
public int hashCode():返回对象的哈希值
同一个对象多次调用hashCode()方法返回的哈希值是相同的 默认情况下,不同对象的哈希值是不同的
判断当前是否为null,如果是null,直接存入,不为null,表示有元素,则调用equals方法比较,如果一样,不存,不一样,存入数组
JDK7新元素占老元素位置,指向老元素,JDK8新元素挂在老元素下面
当挂在元素下面的数据过多时,查询性能降低,从JDK8开始,当链表长度超过8时,自动转换为红黑树
创建一个默认长度为16,默认加载因子为0.75的数组,数组名table
判断当前位置是否为null,如果是null直接存入,如果位置不为null,表示有元素,则调用equals方法比较属性,如果一样,则不存,如果不一样,则存入数组
当数组存满到16*0.75时,就自动扩容,每次扩容原先的两倍
重写对象的hashCode和equals方法(Set集合去重复的原理是 先去判断哈希值,再去判断equals)
在自己定义的Student类中,alt+insert 选择要重写的
原理 底层数据结构依然是哈希表 只是每个元素有额外多了一个双链表的机制记录存储的顺序
对于数值类型:Integer,Double,官方默认按照大小进行升序排序
对于自定义类型如Student对象,TreeSet无法直接排序
TreeSet集合存储对象的时候有2种方式可以设计自定义比较规则
方式1 让自定义类实现 Comparable 接口 重写里面的compare To 方法来制定比较规则
方式2 TreeSet 集合有参数构造器,可以设置Comparator接口对应的比较器对象,来指定比较规则
作用 传输参数非常灵活,方便。可以不传输参数,也可以传输不定个数的内容,也可以传输一个数组
注意事项 一个形参列表中可变参数只能有一个 可变参数必须放在形参列表后满
public static <T> boolean addAll(Collection<? super T> c ,T…Elements) 给集合对象批量添加元素
public static void shuffle(List<?> list) 打乱List集合元素的顺序
适用范围 只对于List 集合 Set集合是根据自己的哈希值来定位的
排序方式1 public static <T> void sort(List<T> list) 将集合中的元素按照默认规则排序
本方式不可以直接对自定义的类型List集合排序,除非自定义类型实现了比较规则Comparable 接口
排序方式2 public static <T> void sort(List<T> list,Comparator<? super T> c ) 将集合中的元素按照指定那个规则排序
按照价格,由于价格是double类型的,而我们定义的是int类型的,所以用类型的Double
Map集合是一种双列集合,每个元素包含两个数据 Map集合的每个元素的格式:key=value(键值对元素) Map集合也被称为”键值对集合”
Collection结合的格式 [元素1,元素2,元素3…]
Map集合格式{key1=value1,key2=value2,key3=value3,…}
Map集合的见是无序,不重复,无索引,值不做要求(可以重复)
HashMap : 元素按照 键 是无序,不重复,无索引,值 不做要求
LinkedHashMap:元素按照 键 是有序,不重复,无索引,值 不做要求
TreeMap: 元素按照 键 是排序,不重复,无索引,值 不做要求
Map是双列集合的祖宗接口,它的功能是全部双列集合都可以继承使用的
V remove(Object key) 根据键值对删除元素
boolean containsKey(Object key) 判断集合是否包含指定的键
boolean containValue(Object value) 判断集合是否包含指定的值
先获取Map集合的全部键的Set集合 遍历键的Set集合,然后通过键提取对应值
设计的API Set<K> keySet()获取所有键的集合 V get(Object key)根据键获取值
先把Map集合转换成Set集合,Set集合中每个元素都是键值对实体类型了 先遍历Set集合,然后提取键以及提取值
Set<Map.Entry<K,V>>entrySet() 获取所有键值对对象的集合
default void forEach(BiConsumer<? super K,? super V> action)
HashMap的特点和底层原理(其实Set底层就是Map,只不过只有键,没有值罢了)
由键决定 无序、不重复、无索引 HashMap底层是哈希表结构
如果键要存储的是自定义对象,要重写hashCode和equals方法
原理 底层数据结构依然是哈希表,只是每个键值对元素有额外多了一个双链表的机制记录存储的顺序
可排序 按照键数据的大小默认升序排序,只对键排序,也可以自己定义比较规则 TreeSet时讲过,当我们选择用谁来比较是,就以谁为一样做相同的判断标志 例如用weight,如果两个weight一样,那就视为重复
不可变集合,就是不可被修改的集合 集合的数据项在创建的时候提供,并且在整个声明周期中都不可改变
如果某个数据不能被修改,把它防御性地拷贝到不可变集合中是个很好的实践
在List、Set、Map接口中,都存在of方法,可以创建一个不可变的集合
static <E> List<E> of(E…elements) 创建List集合对象
static <E> Set<E> of(E…elements) 创建Set集合对象