集合框架
概念
Java集合框架(Java Collections Framework简称JCF)是为表示和操作集合,而规定的一种统一的标准的体系结构。集合框架包含三大块内容:对外的接口、接口的实现和对集合运算的算法。
集合就是用于存储对象的容器。 只要是对象类型就可以存进集合框架中。集合的长度是可变的。 泛型集合中不可以存储基本数据类型的值
集合和数组的区别
数组和集合相比,数组的缺点是它长度是固定的,没有办法动态扩展。
而集合存储数据时是没有长度限制的,是可以动态扩展的。集合容器因为内部的数据结构不同,有多种不同的容器对象。这些容器对象不断的向上抽取,就形成了集合框架。
Collection 接口
Collection接口是单值集合的顶层接口,它的继承关系如下。
List接口及其实现类
特点
- List集合是有序集合: 数据的添加和存储次序一致
- List集合可以存储重复的数据
- List集合中的数据可以通过下标访问
ArrayList实现类
特点:
- 实现了List接口
- 可以动态扩容(我们只管存,长度不够,底层会自动的扩容)
- 通过下标可以快速访问数据
- 查找快,插入删除慢
- ArrayList底层是数组,对数组做了封装
- 可以存储任意类型的数据,包括null
- 数据按照存储次序排列
- 数据可以重复
- 多线程访问时不安全
ArrayList的创建和使用
//List:1.可以按索引访问元素 2.允许重复
List list = new ArrayList();list.add("aaa");
list.add(123);
list.add(234.234);
list.add(true);
list.add(new Car("宝马",200));
list.add(new Car("奔驰",100));
// 输出System.out.println(list);
遍历集合: for+索引遍历
// 遍历方式1: for + 索引
for(int i=0;i<list.size();i++){
// list中默认装进去之后都变为Object类型的数据
Object o = list.get(i);
System.out.println(o);
}
遍历集合: for Each遍历
// 遍历方式2:
for Eachfor (Object o : list){
System.out.println(o);
}
ArrayList底层源码第十章 集合框架 3.3.3
LinkedList和Vector
LinkedList: 具有List的特征,底层以链表结构实现,可以进行头尾元素的添加删除
Vetor: 与ArrayList功能一致,在多线程环境下,比ArrayList安全。性能比较低。
Set接口及其实现类
Set接口特点
- Set接口是无序的
- Set接口中的数据不允许重复
- Set接口无法通过下标访问数据
- 查找慢,插入删除快(底层数据结构是哈希表、链表和红黑树)
- Set集合使用equals()和hashCode()方法实现元素去重
HashSet实现类
HashSet特点:
- HashSet是Set接口的实现类
- 线程不安全
HashSet避免对象重复的规则:
1)如果对象的hashCode值不同,则不用判断equals方法,就直接存到HashSet中。
2)如果对象的hashCode值相同,需要用equals方法进行比较,如果结果为true,则视为相同元素,不存储。如果结果为false,视为不同元素,进行存储。
注意:如果对象元素要存储到HashSet中,必须覆盖hashCode方法和equals方法。才能保证从对象中的内容的角度保证唯一。
迭代器Iterator
迭代器(iterator)有时又称游标(cursor)是程序设计的软件设计模式,可在容器对象(container,例如链表或数组)上遍访的接口,设计人员无需关心容器对象的内存分配的实现细节。
HashSet类中没有提供根据集合索引获取索引对应的值的⽅法,
因此遍历HashSet时需要使⽤Iterator迭代器。Iterator的主要⽅法如下
TreeSet
TreeSet 基于TreeMap 实现。TreeSet可以实现有序集合,但是有序性需要通过比较器实现。
TreesSet特点:
- 有序
- 不重复
- 添加、删除、判断元素存在性效率比较高
- 线程不安全
TreeSet对元素进行排序的方式:
1) 如果是基本数据类型和String类型,无需其它操作,可以直接进行排序。
2) 对象类型元素排序,需要实现Comparable接口,并覆盖其compareTo方法。
3) 自己定义实现了Comparator接口的排序类,并将其传给TreeSet,实现自定义的排序规则。
LinkedHashSet
LinkedHashSet是一种有序的Set集合,其元素的存入和取出顺序是相同的。
Map接口及其实现类
Map接口特点:
- 以键值对方式存储数据(Collection是单值集合)
- 键不能重复,键重复时,后面的数据会覆盖前面的数据
- HashMap的鍵和值可以存储null。Hashtable不能存儲null值。
- 键值对数据无序
HashMap实现类
HashMap实现了Map接口,拥有Map接口的基本特点。HashMap线程不安全,效率高。HashMap的底层是由哈希表、链表加红黑树构成的。
HashMap底层原理
HashMap的put()和get()的实现
1) map.put(key,value)实现原理
第一步:首先将k,v封装到Node对象当中(节点)。
第二步:它的底层会调用K的hashCode()方法得出hash值。
第三步:通过哈希表函数/哈希算法,将hash值转换成数组的下标,下标位置上如果没有任何元素,就把Node添加到这个位置上。如果说下标对应的位置上有链表。此时,就会拿着k和链表上每个节点的k进行equals。如果所有的equals方法返回都是false,那么这个新的节点将被添加到链表的末尾。如其中有一个equals返回了true,那么这个节点的value将会被覆盖。
2) map.get(key) 实现原理
第一步:先调用k的hashCode()方法得出哈希值,并通过哈希算法转换成数组的下标。
第二步:通过上一步哈希算法转换成数组的下标之后,在通过数组下标快速定位到某个位置上。重点理解如果这个位置上什么都没有,则返回null。如果这个位置上有单向链表,那么它就会拿着参数K和单向链表上的每一个节点的K进行equals,如果所有equals方法都返回false,则get方法返回null。如果其中一个节点的K和参数K进行equals返回true,那么此时该节点的value就是我们要找的value了,get方法最终返回这个要找的value。
HashMap底層原理参考:
全网把Map中的hash()分析的最透彻的文章,别无二家。-HollisChuang's Blog
Hashmap实现原理及扩容机制详解_lkforce的博客-CSDN博客_hashmap扩容
Hashtable
Hashtable也实现了Map接口,但是与HashMap相比有如下特点:
- 以键值对方式存储数据
- 键不能重复
- 数据无序
- 键和值都不能存储null
- 线程安全,效率低
泛型集合
泛型:对要管理的数据进行类型限定,方便数据的处理。
为什么实用泛型:
在往集合中存储数据的时候缺乏类型检查,不安全
泛型:类型检查机制;
好处:省略了装箱和拆箱,效率高;类型安全。
List<T>:E类型约束
Set<T> : E类型约束
Map<K,V>:K,V类型约束
自定义链表
链表是最简单的动态数据结构,数据存储在节点(Node)中,其节点的数据结构如下:
class Node{
Object obj;//数据存储的地方
Node next;//也是一个节点,他指向当前节点的下一个节点
}
我们可以把链表理解成为一个火车,每个链表,其实就是一节车厢,数据存储在车厢中中,而每个火车节都有一个指针,连接着下一个火车节。
链表有一个优点:
真正的动态数据结构,无需关心创建的空间是否过大,不需要像数据一样担心容量的问题。
缺点:
不能像数组那样,给一个索引就能查找到指定的值。
1.单向链表
单向链表就是通过每个结点的指针指向下一个结点从而链接起来的结构,最后一个节点的next指向null。
2.单向循环链表
单向循环链表和单向列表的不同是,最后一个节点的next不是指向null,而是指向head节点,形成一个“环”。
3.双向链表
从名字就可以看出,双向链表是包含两个指针的,pre指向前一个节点,next指向后一个节点,但是第一个节点head的pre指向null,最后一个节点的tail指向null。
4.双向循环链表
双向循环链表和双向链表的不同在于,第一个节点的pre指向最后一个节点,最后一个节点的next指向第一个节点,也形成一个“环”。而LinkedList就是基于双向循环链表设计的。