数据结构
- 分类
-
线性结构(至少0个直接前继/后继结点,至多1一个直接前继/后继结点)
- 顺序表
- 链表
- 栈:后进先出(属于访问受限的结构,LIFO:last-in,first-out)
- 队列:先进先出(属于访问受限的结构,FIFO:first-in,first-out)
-
树结构(至少0个,至多1个直接前继结点;至少0个,至多n个直接后继结点[n>=2])
-
图结构(0 至n个直接前继和直接后继(n大于或等于2))
- 简单图
- 多重图
- 有向图
- 无向图
-
哈希结构(没有直接前继/后继结点;查询效率高)
-
- 数据处理能力的考量因素
- 空间复杂度
- 时间复杂度
算法时间复杂度是种衡量计算性能的指标,反映了
程序执行时间随
输入规模增长而增长的量级,在很大程度上能够反映出算法性能的优劣与否
- 量级通常用大写的0和一个函数描述,如O ( n3 ) 表示程序执行时间随输入规模呈现三次方倍的增长,这是比较差的算法实现。
- 从最好到最坏的常用算法复杂度排序如下:常数级0 (1) 、对数级O (logn) 、线性级 、线性对数级、(nlogn) 、平方级、立方级O(n3)、指数级0(2") 等。
2^3 = 8 --> log2^8 = 3;
log2^100 --> 2^x = 100
集合框架
- 基本概念
- 集合框架通常包含 :接口 实现 算法 三部分,Java集合类库将接口和实现分离。
- 集合框架定义了统一的一组接口,并提供了较为高效的标准实现,通过这些方法,我们可以实现数据结构,操作集合中的数据。当然也可以通过实现接口来自己写集合类
- 使用接口来存放集合的引用,接口本身不能说明哪种实现效率更高,具体引用哪种类取决于需要构建的集合对象
接口如: List Set Queue
标准实现如: arrayList linkedList TreeSet HashSet 等
- 体系分类
- 按照单个元素存储的collection(list/set都实现collection接口;
- 按照key-value存储的map
- iterator
- 迭代器的模型:迭代器位于两个元素之间
- 迭代器在操作元素时,必须已经携带元素,即需要先调用next 或previous
- 因为collection 拓展了iterable接口,所以标准类库中的集合都可以使用foreach循环
- lambda iterator.forEachRemaining(e -> do something)
- 具体实现类
collection实现类:
ArrayList
- 可以动态伸缩的索引序列
LinkedList
- 可以在任意位置高效增删元素的有序序列
ArrayDeque
- 用循环数组实现的双端队列
HashSet
- 无重复元素的无序集合
TreeSet
- 无重复的有序集合
EnumSet
- 包含枚举类型元素的集
LinkedHashSet
- 可以记住元素插入次序的集合
PriorityQueue
- 允许高效删除最小元素的集合
map实现类:
HashMap
- 存储键值
TreeMap
- 键值有序排列的映射表
EnumMap
- 键值属于枚举类型的映射表
LinkedHashMap
- 可以记住键值对添加次序的映射表
WeakHashMap
- 值没有对应键后,可以被垃圾回收器回收的映射表
IdentityHashMap
- 使用==而不是用equals比较键值的映射表
- 常用集合类型
-
List
- 遍历结果是稳定的
- 常用实现:
- arrayList
- 内部实现:使用数组存储的顺序表结构。(未指定容量时,创建空数组。首次add元素时,默认扩容10,并执行数组复制)
- 线程安全:否
- 快速随机访问(RandomAccess):支持
- 插入删除:较慢(依次向后或向前移动位置)
- 优点:查询快速
- subList() 方法返回的list是arrayList的内部类,无法序列化,所以不能网络传输
- linkedList
- 内部实现:双向链表(没有初始化容量,不用扩容,只有一个传入集合的构造方法。链表保存first、last元素。Node内部类包含prev/item/next属性。每个元素都是一个Node类)
- 线程安全:否(多线程访问时抛出ConcurrentModificationException)
- 快速随机访问:不支持
- 插入删除:较快(依次向后或向前移动位置)
- 额外实现:Deque接口同时具有队列和栈的性质
- 重要成员:size/first/last
- 优点:通过附加引用将零散的内存单元关联形成可按链路顺序查找的线性结构,内存利用率较高
- arrayList
-
Queue
- 先进先出,一端插入,另一端获取;特殊线性表
- 没有元素即空队列
- FIFO+阻塞操作的特性,所有常被用作数据缓冲区(buffer)
-
Set
- 不允许重复
- 常用实现:
- hashSet
- 描述:value固定为静态对象,使用key保证元素唯一
- 内部实现:hashMap
- linkedHashSet
- 描述:继承自hashSet,具有其优点
- 内部实现:链表维护了元素的插入顺序
- treeSet
- 描述:按照某种比较规则将元素插入合适位置以保证有序
- 内部实现:treeMap,底层使用树结构
- hashSet
-
Map
- 键值对作为存储元素实现的哈希结构
- key唯一,value可重复
- keySet():查看所有key
- values():查看所有value
- entrySet():查看所有键值对
- hashMap中key/value允许weinull,concurrentHashMap不允许。任何map类集合中都应尽量避免kv设为null。
- 常用实现:
- hashTable(线程安全,性能瓶颈,已被淘汰)
- hashMap
- 内部实现:哈希结构
- 线程安全:否
- concurrentHashMap
- 内部实现:
- 线程安全:是(多线程推荐)
- treeMap key有序的map集合
-
线程安全的集合:vector、stack、hashTable、enumeration(网友总结:喂,SHE)
-
- 集合初始化
- 分配容量
- 设置特定的初始化参数等
- 举例:
- ArrayList
- 初始大小10
- 扩容时,创建新数组再复制
- hashMap
- 初始大小16
- 扩容时,需要重建hash表
- ArrayList
- 集合与数组
- 数组遍历
// 优先使用foreach,
// for(int i = 0;i<arr.length;i++){}
// 函数式接口编程
Arrays.asList(args3).stream().forEach(x - > System.out.println(x));
Arrays.asList(args3).stream().forEach(System.out::println);
- 相互转换
- 数组转集合
List list = Arrays.asList(arr);
不能修改集合list的个数(add/remove/clear),否则抛异常asList返回的Arrays的内部类,该类未实现集合修改元素个数的方法;可以set修改元素(实质还是操作数组)
- 集合转数组
-
集合中的泛型
- JDK5之后出现,为了集合的类型安全问题
- List<?>一般作为参数来接收外部的集合,或者返回一个不知道具体元素类型的集合。
- List只能放置一种类型,放置多种类型:<? extends T >与<? super T >
- <? extends T >是Get First,适用于消费集合元素为主的场景;
- <? super T>是Put First,适用于生产集合元素为主的场景。
-
元素比较
- hashCode()和equals() 一并重写的问题:(保持逻辑一致;直接使用equals效率较低;直接使用hashCode,hash碰撞又不可避免。使用hashCode,能过滤绝大多数情况,从而提高效率)
- comparable接口(compareTo)
- 实现该接口的类内部
自营性的
比较器,重写compareTo方法,自定义排序规则。 - 后期不适合修改比较逻辑
- 实现该接口的类内部
- comparator接口(compare)
- 实现comparator接口,重写compare方法
平台性的
比较器(定义在外部,独立于某个类)- 应用:Arrayo.sort(T[]a,Comparator<? super T>),作为比较器参数传入
- 对象比较,尽量避免实例调用equals,易抛出NPE。推荐使用java.util.Objects.equals(obj,obj2)
-fail-fast机制
-fail-safe机制(保存快照)
-
集合框架中的两大容器(基本接口) collection 和 map
Java中没有collection的直接实现类,只提供了collection子接口的实现类
注意
-
循环数组是一个有界集合,容量有限。如果收集的对象数量没有上限,最好使用链表来实现
-
Java集合中的迭代器,remove() 必须跟在访问一个元素之后执行[删除时必须知道删除哪一个,否则抛出异常]
-
list支持快速随机访问,链表不支持快速随机访问,必须从头开始遍历,没有捷径。随机访问很慢,最好使用迭代器
-
RandomAccess是一个标记接口,实现了该接口的类或接口,都支持随机访问(LinkedList没有实现该接口,所以不能随机访问)
-
数组中插入或删除元素时代价很大,因为需要将新元素后面的所有元素向前或向后移动
-
链表是双向链接的有序集合,即每个节点(内部类Node实现)存放下一个节点的引用,同时也存放前驱后驱节点的引用[Ele(
data
,next
,previous
)]。当删除中间某个节点时,只需修改上一个节点中的next 和下个节点中的previous即可 -
list的内部类实现了 ListIterator 接口,可以使用ListIterator 从前后两个方向遍历链表
-
iterator 只能删除元素,但是listIterator可以在插入元素
-
迭代器中,add方法只依赖于迭代器位置,remove方法则依赖于迭代器状态,即hasNext() 是false还是true
-
迭代器中,使用set来修改迭代的元素
-
一个集合有多个迭代器时,集合元素被删除时,后面的迭代器会抛出异常
-
迭代器只能向后遍历【hasNext、next】,列表迭代器listIterator可以双向遍历【hasPrevious、previous、hasNext、next】
参考
- 码出高效
- Java核心技术卷1