Java之 集合

1. 类集概述(重点)

  • 普通的对象数组的最大问题在于数组中的元素个数是固定的,不能动态的扩充大小,所以最 早的时候可以通过链表实现一个动态对象数组。但是这样做毕竟太复杂了,所以在 Java 中为了方便用户操作各个数据结构, 所以引入了类集的概念,有时候就可以把类集称为 java 对数据结构的实现。
  • 类集中最大的几个操作接口:Collection、Map、Iterator,这三个接口为以后要使用的最重点的接口。
  • 所有的类集操作的接口或类都在 java.util 包中。
    在这里插入图片描述

2. 链表与二叉树思路

链表的概念: 链表 [Linked List]是由一组不必相连(不必相连:可以连续也可以不连续)的内存结构(节点),按特定的顺序链接在一起的抽象数据类型。
补充:

  • 抽象数据类型(Abstract Data Type [ADT]):表示数学中抽象出来的一些操作的集合。
  • 内存结构:内存中的结构,如:struct、特殊内存块…等等之类;

2.1 数组和链表的区别和优缺点:

数组是一种连续存储线性结构: 元素类型相同,大小相等
1) 数组的优点:

  1. 存取速度快

2) 数组的缺点:

  1. 事先必须知道数组的长度
  2. 插入删除元素很慢
  3. 空间通常是有限制的
  4. 需要大块连续的内存块
  5. 插入删除元素的效率很低。

链表是离散存储线性结构:
n个节点离散分配,彼此通过指针相连,每个节点只有一个前驱节点,每个节点只有一 个后续节点,首节点没有前驱节点,尾节点没有后续节点。
1) 链表优点:

  1. 空间没有限制
  2. 插入删除元素很快

2) 链表缺点:

  1. 存取速度很慢

2.2 三种链表类型:单链表、双向链表、循环链表。

在这里插入图片描述
链表的核心操作集有 3 种:插入、删除、查找(遍历)。
单链表

  • 单链表 [Linked List]:由各个内存结构通过一个 Next 指针链接在一起组成,每一个内 存结构都存在后继内存结构(链尾除外),内存结构由数据域和 Next 指针域组成。
  • 解析: Data 数据 + Next 指针,组成一个单链表的内存结构 ; 第一个内存结构称为 链头,最后一个内存结构称为 链尾; 链尾的 Next 指针设置为 NULL [指向空]; 单链表的遍历方向单一(只能从链头一直遍历到链尾)

双向链表

  • 双向链表 [Double Linked List]:由各个内存结构通过指针 Next 和指针 Prev 链接在一 起组成,每一个内存结构都存在前驱内存结构和后继内存结构(链头没有前驱,链尾没有后 继),内存结构由数据域、Prev 指针域和 Next 指针域组成。
  • 解析: Data 数据 + Next 指针 + Prev 指针,组成一个双向链表的内存结构; 第一个内存结构称为 链头,最后一个内存结构称为 链尾; 链头的 Prev 指针设置为 NULL, 链尾的 Next 指针设置为 NULL; Prev 指向的内存结构称为 前驱, Next 指向的内存结构称为 后继; 双向链表的遍历是双向的,即如果把从链头的 Next 一直到链尾的[NULL] 遍历方向定 义为正向,那么从链尾的 Prev 一直到链头 [NULL ]遍历方向就是反向;

循环链表

  • 单向循环链表 [Circular Linked List] : 由各个内存结构通过一个指针 Next 链接在一起 组成,每一个内存结构都存在后继内存结构,内存结构由数据域和 Next 指针域组成。
  • 双向循环链表 [Double Circular Linked List] : 由各个内存结构通过指针 Next 和指针 Prev 链接在一起组成,每一个内存结构都存在前驱内存结构和后继内存结构,内存结构由 数据域、Prev 指针域和 Next 指针域组成。
  • 解析: 循环链表分为单向、双向两种; 单向的实现就是在单链表的基础上,把链尾的 Next 指针直接指向链头,形成一个闭环; 双向的实现就是在双向链表的基础上,把链尾的 Next 指针指向链头,再把链头的 Prev 指针指向链尾,形成一个闭环; 循环链表没有链头和链尾的说法,因为是闭环的,所以每一个内存结构都可以充当链头 和链尾;

2.3 二叉树的概念:

二叉树是树的一种,每个节点最多可具有两个子树,即结点的度最大为 2(结点度:结点拥 有的子树数)。一般为有序二叉树,即左子树比节点小右子树比节点大。
在这里插入图片描述
树的一些概念:
在这里插入图片描述

  • 解析:二叉树就是每个节点不能多于有两个儿子,上面的图就是一颗二叉树,而且还是一种特殊的 二叉树:二叉查找树(binary search tree)。
  • 定义:当前根节点的左边全部比根节点小,当前根节点的右边全部比根节点大。
    一棵树至少会有一个节点(根节点)

二叉树的种类:斜树、满二叉树、完全二叉树

  1. 斜树:所有结点都只有左子树,或者右子树。
    左斜树

  2. 满二叉树:所有的分支节点都具有左右节点。
    在这里插入图片描述

  3. 完全二叉树:若设二叉树的深度为 h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。
    在这里插入图片描述

二叉树的一些性质:

  1. 二叉树第 i 层上的结点数目最多为 2^(i-1) (i≥1)
  2. 深度为 h 的二叉树至多有 2^h-1 个结点(h≥1)
  3. 包含 n 个结点的二叉树的高度至少为 log2 (n+1)
  4. 在任意一棵二叉树中,若终端结点的个数为 n0,度为 2 的结点数为 n2,则 n0=n2+1

二叉树的遍历方式:

  • 先序遍历:先访问根节点,然后访问左节点,最后访问右节点(根->左->右)
  • 中序遍历:先访问左节点,然后访问根节点,最后访问右节点(左->根->右)
  • 后序遍历:先访问左节点,然后访问右节点,最后访问根节点(左->右->根)
    例如:
    在这里插入图片描述
    先序遍历(根-左-右):1-2-4-8-9-5-10-3-6-7
    中序遍历:(左-根-右):8-4-9-2-10-5-1-6-3-7
    后序遍历(左-右-根):8-9-4-10-5-2-6-7-3-1

3. 常见数据结构

数据存储的常用结构有: 栈、队列、数组、链表和红黑树。

3.1 栈

概念: 栈:stack,又称堆栈, 栈(stack)是限定仅在表尾进行插入和删除操作的线性表。我们把允许插入和删除的一端称为栈顶,另一端称为栈底,不含任何数据元素的栈称为空栈。栈又称为先进后出 的线性表。
采用该结构的集合,对元素的存取有如下的特点:

  • 先进后出(即,存进去的元素,要在后它后面的元素依次取出后,才能取出该元素)。例如,子弹 压进弹夹,先压进去的子弹在下面,后压进去的子弹在上面,当开枪时,先弹出上面的子弹,然后 才能弹出下面的子弹。
  • 栈的入口、出口的都是栈的顶端位置。

压栈: 存元素。
弹栈: 取元素。

在这里插入图片描述

3.2 队列:

概念: 队列:queue,简称队, 队列是一种特殊的线性表,是运算受到限制的一种线性表,只允许在表的 一端进行插入,而在另一端进行删除元素的线性表。 队尾(rear)是允许插入的一端。队头(front)是 允许删除的一端。 空队列是不含元素的空表。
采用该结构的集合,对元素的存取有如下的特点:

  • 先进先出(即,存进去的元素,要在后它前面的元素依次取出后,才能取出该元素)。例如,小火 车过山洞,车头先进去,车尾后进去;车头先出来,车尾后出来。
  • 队列的入口、出口各占一侧。例如,下图中的左侧为入口,右侧为出口。
    在这里插入图片描述

3.3 数组:

概念: 数组:Array,是有序的元素序列,数组是在内存中开辟一段连续的空间,并在此空间存放元素。就 像是一排出租屋,有100个房间,从001到100每个房间都有固定编号,通过编号就可以快速找到 租房子的人。
采用该结构的集合,对元素的存取有如下的特点:

  1. 查找元素快:通过索引,可以快速访问指定位置的元素
  2. 增删元素慢
    • 指定索引位置增加元素:需要创建一个新数组,将指定新元素存储在指定索引位置,再把原 数组元素根据索引,复制到新数组对应索引的位置。
    • 指定索引位置删除元素:需要创建一个新数组,把原数组元素根据索引,复制到新数组对应 索引的位置,原数组中指定索引位置元素不复制到新数组中。

3.4 链表

概念: 链表:linked list,由一系列结点node(链表中每一个元素称为结点)组成,结点可以在运行时i动 态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的 指针域。我们常说的链表结构有单向链表与双向链表,那么这里给大家介绍的是单向链表。
在这里插入图片描述
采用该结构的集合,对元素的存取有如下的特点:

  1. 多个结点之间,通过地址进行连接。例如,多个人手拉手,每个人使用自己的右手拉住下个人的左 手,依次类推,这样多个人就连在一起了。
  2. 查找元素慢:想查找某个元素,需要通过连接的节点,依次向后查找指定元素
  3. 增删元素快:增加元素:只需要修改连接下个元素的地址即可。
  4. 删除元素:只需要修改连接下个元素的地址即可。

3.5 红黑树

概念:

  • 二叉树:binary tree ,是每个结点不超过2的有序树(tree)。简单的理解,就是一种类似于我们生活中树的结构,只不过每个结点上都最多只能有两个子结点。 二叉树是每个节点最多有两个子树的树结构。顶上的叫根结点,两边被称作“左子树”和“右子树”。
  • 我们要说的是二叉树的一种比较有意思的叫做红黑树,红黑树本身就是一颗二叉查找树,将节点插入 后,该树仍然是一颗二叉查找树。也就意味着,树的键值仍然是有序的。

红黑树的约束:

  1. 节点可以是红色的或者黑色的
  2. 根节点是黑色的
  3. 叶子节点(特指空节点)是黑色的
  4. 每个红色节点的子节点都是黑色的
  5. 任何一个节点到其每一个叶子节点的所有路径上黑色节点数相同

红黑树的特点: 速度特别快,趋近平衡树,查找叶子元素最少和最多次数不多于二倍。

4. 集合框架

概念: 集合按照其存储结构可以分为两大类,分别是单列集合 java.util.Collection 和双列集合 java.util.Map。
Collection: 单列集合类的根接口,用于存储一系列符合某种规则的元素,它有两个重要的子接 口,分别是 java.util.List 和 java.util.Set 。其中, List 的特点是元素有序、元素可重 复。 Set 的特点是元素无序,而且不可重复。 List 接口的主要实现类有 java.util.ArrayList 和 java.util.LinkedList , Set 接口的主要实现类有 java.util.HashSet 和 java.util.TreeSet 。

5. Collection集合(接口)(重点)

概念: 是一种单值存储的集合。Collection 接口是在整个 Java 类集中保存单值的最大操作父接口,里面每次操作的时候都只能保存一个对象的数据。 此接口定义在 java.util 包中。此接口使用了泛型技术。常用子类:List:ArrayList、Vector、LinkedList,Set:HashSet、TreeSet
Collection接口定义格式: public interface Collection<E> extends Iterable
常用方法:
本接口中一共定义了 15 个方法,那么此接口的全部子类或子接口就将全部继承以下接口中的方法。
但是,在开发中不会直接使用 Collection 接口。而使用其操作的子接口:List、Set。为了更加清楚的区分集合中是否允许有重复元素。

No返回值类型方法描述
1public booleanadd(E e)向集合中插入一个元素
2public voidclear()清空集合中的元素
3public booleanremove(Object o)从集合中删除一个对象
4public booleancontains(Object o)查找一个元素是否存在
5public booleanisEmpty()判断集合是否为空
6public intsize()返回集合中元素个数
7public Object[]toArray()把集合中的元素,存储到数组中
8public booleanaddAll(Collection<? extends E>c)向集合中插入一组元素
9public static voidshuffle(List list)打乱集合顺序。
10public static voidsort(List list)将集合中元素按照默认规则排序。
11public static voidsort(List list,Comparator )将集合中元素按 照指定规则排序。
12public booleancontainsAll(Collection<?>c)查找一组元素是否存在
13public Iterator< E >iterator()为 Iterator 接口实例化
14booleanremoveAll(Collection<?>c)从集合中删除一组对象
15booleanretainAll(Collection<?>c)判断是否没有指定的集合
16< T >T[]toArray(T[]a)指定操作的泛型类型,并把内容返回
17public booleanequals(Object o)从 Object 类中覆写而来
18public inthashCode()从 Object 类中覆写而

6. List接口(重点)

概念: java.util.List 接口继承自 Collection 接口,在整个集合中 List 是 Collection 的子接口,是单列集合的一个重要分支,习惯性地会将实现了 List 接口的对象称为List集合。 此接口上依然使用了泛型技术。 子类有ArrayList、Vector、LinkedList等。
List集合特点: 允许元素重复,以线性存储,在程序中可以通过索引来访问集合中的指定元素,元素有序,即元素的存入顺序和取出顺序一致。
List子接口的定义格式: public interface List<E> extends Collection<E>
实例化格式: List<E> = new List<E>();
此接口常用的实现类有如下几个: ArrayList(95%)、Vector(4%)、LinkedList(1%)
常用方法:
在 List 接口中有以下 10 个方法是对已有的 Collection 接口进行的扩充

No.返回值类型方法描述
1public voidadd(int index,E element)在指定位置处增加元素
2booleanaddAll(int index,Collection<? extends E> c)在指定位置处增加一组元素
3public Eget(int index)根据索引位置取出每一个元素
4public intindexOf(Object o)根据对象查找指定的位置,找不到返回-1
5public intlastIndexOf(Object o)从后面向前查找位置,找不到返回-1
6public ListIterator< E >listIterator()返回 ListIterator 接口的实例
7public ListIterator< E > listIterator(int index)返回从指定位置的ListIterator接口的实例
8public Eremove(int index)删除指定位置的内容
9public Eset(int index,E element)修改指定位置的内容
10List< E >subList(int fromIndex,int toIndex)返回子集

7. ArrayList(List子类)

学习内容:查看add的源码,创建时长度为10,添加至满时每次会扩容0.5倍长度,新扩容长度大于int类型最大值-8,如果发现实际需要的长度大于int类型最大值-8,返回int类型最大值,否则返回int类型最大值-8长度。
概念: 是List子类。 java.util.ArrayList 集合数据存储的结构是数组结构是异步的。元素增删慢,查找快,由于日常开发中使用 最多的功能为查询数据、遍历数据,所以 ArrayList 是最常用的集合。 许多程序员开发时非常随意地使用ArrayList完成任何需求,并不严谨,这种用法是不提倡的。是线程不安全的实现。
ArrayList类定义格式: public class ArrayList<E> extends AbstractList<E> implements List, RandomAccess, Cloneable, Serializable
实例化格式: List<E> = new ArrayList<E>();或ArrayList<E> = new ArrayList<E>();

8. Vector(List子类)

概念: 是List子类。 与新集合实现不同,Vector是同步的,如果不需要线程安全的实现,建议用ArrayList代替Vector。其他操作基本与Array List相同,其还提供了对于扩容容量大小的指定和相关方法。如果扩容容量设置为0,则每次扩容为翻倍扩容。是线程安全的实现。
构造方法:

构造器描述
Vector()构造一个空向量,使其内部数据数组的大小为 10 ,其标准容量增量为零。
Vector(int initialCapacity, int capacityIncrement)构造具有指定初始容量和容量增量的空向量。

9. Vectoe类和ArrayList类的区别(重点)

区别点推出时间性能输出
ArrayListJDK 1.2 之后推出的,是新的类异步处理,性能较高支持 Iterator、ListIterator
VectorJDK 1.0 时推出的,旧的操作类同步处理,性能较低除了支持 Iterator、ListIterator 输出,还支持 Enumeration 输

10. LinkedList(List子类)

概念: 是List子类。使用的是双向链表结构,对于元素增删快,查找慢。可以模拟栈和队列。
实际开发中对一个集合元素的添加与删除经常涉及到首尾操作,而LinkedList提供了大量首尾操作的方法。
常用方法:

No.返回值类型方法名称描述
1public voidaddFirst(E e)将指定元素插入此列表的开头。
2public voidaddLast(E e)将指定元素添加到此列表的结尾。
3public EgetFirst()返回此列表的第一个元素。
4public EgetLast()返回此列表的最后一个元素。
5public EremoveFirst()移除并返回此列表的第一个元素。
6public EremoveLast()移除并返回此列表的最后一个元素。
7public Epop()从此列表所表示的堆栈处弹出一个元素。
8public voidpush(E e)将元素推入此列表所表示的堆栈。
9public booleanisEmpty()如果列表不包含元素,则返回true。

模拟栈和队列:

LinkedList<Integer> data = new LinkedList<>();
//模拟栈,结果为200
data.push(100);
data.push(200);
Integer i = data.pop();
Sysytem.out.println(i);
//模拟队列,结果为100
data.addFirst(100);
data.addFirst(200);
Integer i = data.removeLast();
Sysytem.out.println(i);

11. lterator与Listlterator接口(迭代器)

注意: 只要是碰到了集合,则输出的时候想都不想就使用 Iterator 进行输出。
概念: 在程序开发中,经常需要遍历集合中的所有元素。针对这种需求,JDK专门提供了一个接口 java.util.Iterator 。 Iterator 接口也是Java集合中的一员,但它与 Collection 、 Map 接口有所 不同, Collection 接口与 Map 接口主要用于存储元素,而 Iterator 主要用于迭代访问(即遍历) Collection 中的元素,因此 Iterator 对象也被称为迭代器。
迭代的概念: 迭代:即Collection集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续再判断,如果还有就再取出出来。一直把集合中的所有元素全部取 出。这种取出方式专业术语称为迭代。
Iterator与ListIterator的不同: Iterator是ListIterator的父类。Iterator用于迭代Collection下所有集合例如List和Set,而ListIterator只能用于迭代List集合。
实例化格式: Iterator<E> 迭代器名 = 集合变量名. iterator();
Iterator常用方法:

变量和类型方法描述
booleanhasNext()如果迭代具有更多元素,则返回 true 。即隐式游标的下一个元素存在。
Enext()返回迭代中的下一个元素。
default voidremove()从底层集合中移除此迭代器返回的最后一个元素(可选操作)。

注意: 在进行迭代输出的时候如果要想删除当前元素,则只能使用 Iterator 接口中的 remove()方法,而不能使用集合中的 remove()方法。否则将出现未知的错误!
ListIterator常用方法:

变量和类型方法描述
voidadd(E e)将指定的元素插入列表(可选操作)。 该元素紧接在next()返回的元素之前插入(如果有),并且在previous()返回的元素之后插入 (如果有)。(如果列表不包含任何元素,则新元素将成为列表中的唯一元素。)新元素在隐式游标之前插入:对next的后续调用不受影响,随后对previous调用将返回新元素。
booleanhasPrevious()如果此列表迭代器在反向遍历列表时具有更多元素,则返回 true 。
Eprevious()返回列表中的上一个元素并向后移动光标位置。
voidset(E e)用指定的元素替换 next()或 previous()返回的最后一个元素(可选操作)。
intnextIndex()返回后续调用 next()将返回的元素的索引。
intpreviousIndex()返回后续调用 previous()将返回的元素的索引。

注意:隐式游标一开始并不指向集合内容,在调用next()之后指向第一个元素。
在这里插入图片描述
eg:

ArrayList<Integer> data = new ArrayList<>();
data.add(1);
data.add(2);

//输出结果为1
Iterator<Integer> iterator1 = data.itertor();
while(iterator1.hasNext()){
	Integer i = iterator1.next();
	System.out.println(i)
}
//输出结果为100  1  200
Iterator<Integer> iterator2 = data.itertor();
iterator2.add(100);	//next()隐式游标前添加元素
iterator2.next()
iterator2.next();//游标下移2个元素
iterator2.set(200);//设置游标位置元素为200
iterator2.previous();
iterator2.previous();
iterator2.previous();//游标上移3个元素,因为在最上面插入了元素100
while(iterator2.hasNext()){
	System.out.print(iterator2.next());
}

迭代器的两种失败:
1) 快速失败: 迭代器遍历集合本身途中,如果有另一条执行路径对集合内容进行改变,则会报出快速失败异常。
2) 安全失败: 指的是这个失败不会出错,遍历的是集合备份的数据,对集合本身进行操作并不会对已备份的遍历造成影响。

12. forEach(增强for循环)

概念: 增强for循环(也称for each循环)是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和集合的。它的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作
格式:

for(元素的数据类型 变量 : Collection集合or数组){
	//写操作代码
}

13. Set(重点)

概念: java.util.Set 接口和 java.util.List 接口一样,同样继承自 Collection 接口,它与 Collection 接口中的方法基本一致,并没有对 Collection 接口进行功能上的扩充,只是比 Collection 接口更加严格了。
Set与 List 接口的不同点: Set 接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。因为此接口没有 List 接口中定义 的 get(int index)方法,所以无法使用循环进行输出。但是可以采用:迭代器、增强for进行输出、toArray()转成数组。
实例化格式: Set<E> 集合名 = new Set<E>();
此接口中有两个常用的子类: HashSet、TreeSet

14. HashSet(Set子类:散列存放)

概念: java.util.HashSet 底层的实现其实是一个 java.util.HashMap 支持。HashSet 是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证元素唯一性的方式依赖于: hashCode 与 equals 方法。HashSet是无序的、散列存储的。
注意:

  • 给HashSet中存放自定义类型元素时,需要重写对象中的hashCode和equals方法,建立自己的比较方式,才能保证HashSet集合中的对象唯一。
  • HashSet保证元素唯一,元素存放进去是没有顺序的,如果要保证有序,可使用在HashSet下面的一个子类 java.util.LinkedHashSet ,它是链表和哈希表组合的一个数据存储结构。
    HashSet存放自定义对象确保对象唯一的例子(复写Object的hashCode()和equals())
//自定义Person类
public class Person implements Comparable<Person>{
	private String name;
	private int age
//重写equals()
	public boolean equals(Object obj){
		if(this == obj){//同个对象
		return true;
	}
	if(!(obj instanceof Person)){//对象类型判断
		return false;
	}
	Person per = (Person)obj;
	if(per.name.equals(this.name) && per.age == this.age){//判断对象属性是否相等
		return true;
	}else{
		return false
	}
}
//重写hashCode()
	public int hashCode(){
		return this.name.hashCode()*this.age;
	}}

//定义TreeSet集合并向里面增加Person对象
Set all = new HashSet<Person>();
all.add(new Person("张三", 10));
all.add(new Person("张三", 10));
all.add(new Person("李四", 11))

使用toArray()的方法进行打印:

Set<String> all = new HashSet<String>();
Object obj[] = all.toArray();	//将集合变为对象数组
fori

但是,以上的操作不好,因为在操作的时候已经指定了操作的泛型类型,那么现在最好的做法是由泛型所指定的类型变为指定的数组。所以应该用<T>T[] toArray(T[]a)

String[] str = all.toArray(new String[]{});	//变为指定的泛型类型数组
fori

15. TreeSet(Set子类:排序的子类)与Comparable比较器(重点)

TreeSet概念: Set接口的TreeSet类本身允许排序,但是对于自定义类的对象进行排序必须在自定义类中实现Comparable接口
Comparable接口: 用前一个存储进去的数据与接下来存储的数据进行比较,return 1表示大于,return -1表示小于,return 0表示等于。
TreeSet存放自定义对象实现排序的例子(实现Comparable接口)

//自定义Person类
public class Person implements Comparable<Person>{
	private String name;
	private int age
	//在其中实现Comparable接口
	public int compareTo(Person per) {
		if (this.age > per.age) {
			return 1;
		} else if (this.age < per.age) {
			return -1;
		} else {
			//如果此处为return 0,则表示两个年龄相等,由于Set中不能存在相同元素所以李四不会被存储进去,所以此时应该也对姓名进行比较。
			return this.name.compareTo(per.name);
		}
	}}
//测试类中定义TreeSet集合并向里面增加Person对象
Set all = new TreeSet<Person>();
all.add(new Person("张三", 10));
all.add(new Person("李四", 10));
all.add(new Person("王五", 11))

16. Comparator比较器

概念: 排序时,两个对象之间比较大小,那么在JAVA中提供了两种比较实现的方式,一种是比较死板的采用 java.lang.Comparable 接口去实现,一种是灵活的当我需要做排序的时候在去选择的 java.util.Comparator 接口完成。
Comparable和Comparator两个接口的区别:

  1. Comparable: 强行对实现它的每个类的对象进行整体排序。 这种排序被称为类的自然排序,类的 compareTo方法被称为它的自然比较方法。只能在类中实现compareTo()一次,不能经常修改类的代码实现自己想要的排序。实现此接口的对象列表(和数组)可以通过Collections.sort(和Arrays.sort)进行自动排序,对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。
  2. Comparator: 强行对某个对象进行整体排序。 可以将Comparator传递给sort方法(如Collections.sort 或 Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用Comparator来控制某些数据结构 (如有序set或有序映射)的顺序,或者为那些没有自然顺序的对象collection提供排序。

实例化格式: public static void sort(List<T> list,Comparator <? Super T>)
Comparator中用于排序的方法:
比较其两个参数的顺序:
public int compare(String o1, String o2)
两个对象比较的结果有三种: 大于,等于,小于。

  • 如果要按照升序排序, 则o1 小于o2,返回(负数),相等返回0,01大于02返回(正数)
  • 如果要按照降序排序 则o1 小于o2,返回(正数),相等返回0,01大于02返回(负数)
    Collection中sort方法排序时使用Comparator比较器自定义排序的例子:
public static void main(String[] args) {
	ArrayList list = new ArrayList();
	list.add("aba");
	list.add("cba");
	//排序方法 按照第一个单词的降序
	Collections.sort(list, new Comparator<String>() {
		@Override
		public int compare(String o1, String o2) {
			return o2.charAt(0) - o1.charAt(0);
		}
	});
	System.out.println(list);
}
结果为:[cba, aba]

17. Map

概念: 是一种双值(键-值)存储的集合。Map集合存储的是一个个的键值对数据。**Map集合的键(key)不可重复。**常用子类:HashMap、LinkedHashMap、Hashtable、ConcurrentHashMap、TreeMap
在这里插入图片描述
实例化格式: Map<K,V>map = new Map<>();
常用方法:

变量和类型方法描述
voidclear()从此映射中删除所有映射(可选操作)。
SetkeySet()返回此映射中包含的键的Set视图。
Collectionvalues()返回此映射中包含的值的Collection视图。
Vput(K key, V value)将指定的值与此映射中的指定键相关联,即存值(可选操作)。
Vremove(Object key)如果存在,则从该映射中移除键的映射(可选操作)。
default Vreplace(K key, V value)仅当指定键当前映射到某个值时,才替换该条目的条目。
intsize()返回此映射中键 - 值映射的数量。
Vget(Object key)返回指定键映射到的值,如果此映射不包含键的映射,则返回 null 。
booleancontainsKey(Object key)如果此映射包含指定键的映射,则返回 true 。
booleancontainsValue(Object value)如果此映射将一个或多个键映射到指定值,则返回 true 。
Set<Map.Entry<K,V>>entrySet()返回此映射中包含的映射的Set视图。即将Map接口变为Set集合。

18. 哈希表概述

概念: 哈希表又称散列表。在JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一 个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率 较低。而JDK1.8中,哈希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,当红黑树中的数据小于(6)时,将红黑二叉树转换为链表。这样大大减少了查找时间。
哈希表的几个特性:

  1. 哈希表:数组+链表
  2. 哈希表中数组的每个存储位置称为哈希桶。
  3. 初始桶数量为16
  4. 数据在哈希表中存储位置为数据的哈希值对哈希表长度求余,余数即为下标
  5. 相同余数的数据存放在同一个哈希桶中,以链表方式存储在原有数据的下一位置
  6. 散列因子0.75:当75%的哈希桶存上数据时,桶数量扩容为原来的两倍。
  7. 当哈希桶中的数据量超过阈值(8)时,将链表转换为红黑二叉树,当红黑树中的数据量小于(6)时,将红黑二叉树转换为链表。
    在这里插入图片描述
    在这里插入图片描述

19. Map集合各子类区别分析

区别点推出时间性能null
HashMapJDK 1.2 之后推出的,新的操作类异步处理,性能较高允许设置为 null
HashtableJDK 1.0 时推出的,旧的操作类同步处理,性能较低不允许设置,否则将出现空指向异常
ConcurrentHashMap采用分段锁机制,保证线程安全,效率又比较高
TreeMap可以排序
LinkedHashMap存储有序,除了在HashMap中存储还会在双向链表中存储

分段锁机制: 多个线程操作同一个哈希桶的时候,则同步处理,如果多个线程操作的是不同的哈希桶,则异步处理。
注意: **使用对象作为键存储在HashMap中时,不要轻易改变对象中的内容,否则找不到对应的数据。**因为HashMap中数据存储位置是按Hashcode存储的,而对象中的Hashcode又是按对象内容进行计算的,改变对象内容会改变对象的Hashcode,从而使数据存储位置发生改变,而由原来的键在HashMap中进行查找就会找不到相应的数据。

20. Collections集合的操作类

概念: Collections 实际上是一个集合的操作类。Collections 实际上是一个集合的操作类。从实际考虑,使用此类操作并不是很方便,最好的做法就是使用各个接口的直接操作的方法完成。此类只是 一个集合的操作类。
定义格式: public class Collections extends Object
常用方法:

Collections.addAll(list, "A", "B", "C");// 向集合list增加元素,list为集合对象

21. JDK9集合新特性

创建不可修改的集合的方法: of()
eg:

List<String> List = List.of(“加油加油!”,”冲鸭!”);
Set<String> set = Set.of(“永不放弃!”,”努力学习!”);
Map<String, String> map = Map.of(“键1,JavaEE,“键2,”我最行!”)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值