java集合框架知识点

集合框架图
在这里插入图片描述
在这里插入图片描述

Collection(单列集合)
List(有序,可重复)

        ArrayList
            底层数据结构是数组,查询快,增删慢(因为:增删后涉及到其他数据的位移)
            线程不安全,效率高
        Vector
            底层数据结构是数组,查询快,增删慢(因为:增删后涉及到其他数据的位移)
            线程安全,效率低
        LinkedList
            底层数据结构是双向链表,查询慢,增删快
            线程不安全,效率高


    Set(无序,唯一)

        HashSet
            底层数据结构是哈希表。
            哈希表依赖两个方法:hashCode()和equals()
            执行顺序:
                首先判断hashCode()值是否相同
                    是:继续执行equals(),看其返回值
                        是true:说明元素重复,不添加
                        是false:就直接添加到集合
                    否:就直接添加到集合
            最终:
                自动生成hashCode()和equals()即可
                
            LinkedHashSet
                底层数据结构由链表和哈希表组成。
                由链表保证元素有序。
                由哈希表保证元素唯一。
        TreeSet
            底层数据结构是红黑树。(是一种自平衡的二叉树)
            如何保证元素唯一性呢?
                根据比较的返回值是否是0来决定
            如何保证元素的排序呢?
                两种方式
                    自然排序(元素具备比较性)
                        让元素所属的类实现Comparable接口
                    比较器排序(集合具备比较性)
                        让集合接收一个Comparator的实现类对象


Map(双列集合)
    A:Map集合的数据结构仅仅针对键有效,与值无关。
    B:存储的是键值对形式的元素,键唯一,值可重复。

    HashMap

        底层数据结构是:

        jdk1.8以下:(数组+单向链表)哈希表

        jdk1.8+:(数组+[单向链表 / 红黑树])哈希表,根据情况会选择链表和红黑树之间进行转换

        线程不安全,效率高
            哈希表依赖两个方法:hashCode()和equals()
            执行顺序:
                首先判断hashCode()值是否相同
                    是:继续执行equals(),看其返回值
                        是true:说明元素重复,不添加
                        是false:就直接添加到集合
                    否:就直接添加到集合
            最终:
                自动生成hashCode()和equals()即可
            LinkedHashMap
            底层数据结构由链表和哈希表组成。
                由链表保证元素有序。
                由哈希表保证元素唯一。

    Hashtable

        底层数据结构是哈希表。线程安全,效率低
            哈希表依赖两个方法:hashCode()和equals()
            执行顺序:
                首先判断hashCode()值是否相同
                    是:继续执行equals(),看其返回值
                        是true:说明元素重复,不添加
                        是false:就直接添加到集合
                    否:就直接添加到集合
            最终:
                自动生成hashCode()和equals()即可

    TreeMap

        底层数据结构是红黑树。(是一种自平衡的二叉树)
            如何保证元素唯一性呢?
                根据比较的返回值是否是0来决定
            如何保证元素的排序呢?
                两种方式
                    自然排序(元素具备比较性)
                        让元素所属的类实现Comparable接口
                    比较器排序(集合具备比较性)
                        让集合接收一个Comparator的实现类对象

1.创建集合的迭代器对象时,该对象指向集合的第一个元素(index=0)

使用集合中的 iterator()方法获取送代器的实现类对象,使用Iterator接口接收(多态)
使用Iterator接口中的方法hasNext来判断还有没有下一个元素
使用Iterator接口中的方法next来取出集合中的下一个元素
Iterator迭代器
(1)Iterator中的使用
创建一个集合对象,对象.iterator()方法创建迭代器对象
// 创建集合
ArrayList sites = new ArrayList();
// 获取迭代器
Iterator it = sites.iterator();

(2)Iterator中的常用方法:
boolean hasNext(): 判断当前位置是否有元素可以被取出,不改变指向元素的位置
E next(): 获取当前位置的元素,将迭代器对象移向下一个索引位置
(3)概念
Java迭代器(Iterator)是 Java 集合框架中的一种机制,是一种用于遍历集合(如列表、集合和映射等)的接口。它提供了一种统一的方式来访问集合中的元素,而不需要了解底层集合的具体实现细节。Java Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法,可用于迭代 ArrayList 和 HashSet 等集合。Iterator 是 Java 迭代器最简单的实现,ListIterator 是 Collection API 中的接口, 它扩展了 Iterator 接口。

增强for循环:快捷方式(list.for回车)
(1)实现iterable的类才能使用增强for循环,collection继承了iterable,单列集合可用。

(2)语法规则:
for(集合/数组中元素的数据类型 变量名 : 集合/数组名){
//已经将当前遍历到的元素封装到变量中了,直接使用变量即可
}

(3)注意事项:使用增强for不能修改集合或数组中的元素,原因是因为在增强 for循环中仅仅是修改了临时变量,并没有改变数组中的元素。

(4)三种循环的使用场景
如果需要操作索引,使用普通for循环
如果在遍历的过程中需要删除元素,使用迭代器
如果仅仅想遍历集合,使用增强for

2.使用迭代器(集合的遍历方法)Iterator遍历集合的元素

forEach离不开Iterable和Iterator
在这里插入图片描述

Iterable是获取一个迭代器,API -> 只有一个方法 iterator() - 获取一个迭代器
迭代器(iterator):
a.作用:遍历/迭代集合(数组)所有元素
b.三个方法:
hashNext() - 询问有没有下一个元素
next() - 移动到下一个元素,并返回该位置上的元素
remove() - 删除集合元素

3.使用迭代器删除元素,remove()删除的是迭代器指向的上一个元素

在这里插入图片描述

4.泛型(java5引入的特性,提供了编译时类型检测机制)的好处

(1)将运行期的问题提前到编译期
(2)避免了强制类型转换

5.类型通配符

类型通配符: <?>

ArrayList<?>: 表示元素类型未知的ArrayList,它的元素可以匹配任何的类型  
但是并不能把元素添加到ArrayList中了,获取出来的也是父类类型  

类型通配符上限: <? extends 类型>

ArrayListList <? extends Number>: 它表示的类型是Number或者其子类型 

类型通配符下限: <? super 类型>

ArrayListList <? super Number>: 它表示的类型是Number或者其父类型 

5.list的实现类:ArrayList和LinkedList

(List—是一个有序的集合,可以包含重复的元素,提供了按索引访问的方式,它继承Collection)
(1)list特点:存取有序、可以重复、有索引
(2)list集合的特有方法(相对于collection的特有的方法):
在这里插入图片描述

6.1 ArrayList(数组结构)

默认开辟的容量大小为0,在无参构造函数创建ArrayList时其实创建的是一个容量为0的数组(DEFAULTCAPACITY_EMPTY_ELEMENTDATA 标识),只有在第一次新增元素时才会被扩容为10。
(1)特点: 底层是数组结构实现,查询快、增删慢
(2)ArrayList 类是一个可以动态修改的集合数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。ArrayList 继承了 AbstractList ,并实现了 List 接口。
在这里插入图片描述

6.2 LinkedList(链表结构、双向链表)

更多用来实现存储,内部存在一个链表节点内部类,没有要求初始长度,存储数据时需要将数据封装进Node节点(Next:下一个节点;Prev:上一个节点)

(1)特点:底层是链表结构实现、查询慢、增删快
(2)LinkedList集合的特有功能
在这里插入图片描述

6.3 vector(数组结构)

默认开辟的容量大小为10
在这里插入图片描述

ArrayList和Vector区别:
(1)ArrayList和Vector都是数组机制,默认开辟的容量大小不同,ArrayList为0,Vector 10

(2)ArrayList的方法都是非同步,也就是异步方法(性能提高、线程不安全),Vector是同步方法(性能降低、线程安全)

(3)ArrayList是在JDK1.2提出的,Vector是1.0的元老级类

7.set(不允许内容重复,调对应的map)

Set 存储自定义类型元素,并要去除重复的元素时,需要在该元素中重写 HashCode 方法和 equals 方法。

子类:
HashSet(散列存储、无序存储,顺序随机)
TreeSet(有序存储,顺序按ASCII码排序)(Comparable、Comparator)

HashSet 是一个无序的集合,它不会维护元素的顺序,因此默认情况下不支持使用 Comparable 或 Comparator 来对元素进行排序。这是因为 HashSet 内部使用哈希表来存储元素,不关心元素的顺序,只关心元素的唯一性和哈希码。 如果你需要对元素进行排序,你应该使用实现有序集合接口的集合类,例如 TreeSet。在 TreeSet 中,你可以使用 Comparable 或 Comparator 接口来定义元素的排序规则,然后元素将按照这个规则进行排序。

排序复写
在这里插入图片描述
LinkedHashSet(链表存储、有序存储,怎么存怎么取)
SortedSet

HashSet存储元素不重复的原理
在这里插入图片描述

8.Arrays(操作数组)、Array

(1)Arrays

Arrays是一个与数组有关的类,提供了大量的静态方法来操作数组。Java中提供了Arrays类协助这几个操作:sort(),binarySearch(),equals(),fill(),asList().
String toString(数组):将数组转换为字符串,默认格式为[元素1,元素2,元素3…]
void sort(数组):对参数数组按照升序(从小到大)进行排序
在这里插入图片描述

(2)Array(java.lang.reflect.Array )

java.lang.reflect.Array类提供静态方法来动态创建和访问Java数组。

Array允许在get或set操作期间扩展转换,但如果发生缩小转换,则会抛出IllegalArgumentException异常。

此类提供了创建和操作数组的一系列静态(static)本地(native)方法。

这些功能即使不通过此类,也可以实现,比如创建数组。

Object arr = Array.newInstance(Integer.class , 2);//使用Array类创建数组
int [] a = new int [2];//不适用Array方法也可以实现创建数组的功能
另外给元素赋值和获取元素值也是这种情况。
那么Array类存在的意义是什么呢?或者说上面两种实现方式有什么不同呢?

为什么会在java.lang.reflect包中
一目了然啊,因为需要动态创建数组的时候,这样只能用反射API啊,

所以此类就有意义啦,所以在reflect中。

9.LinkedList和ArrayList区别

LinkedList和ArrayList的差别主要来自于Array和LinkedList数据结构的不同。如果你很熟悉Array和LinkedList,你容易得出下面的结论:

  1. 因为Array是基于索引(index)的数据结构,它使用索引在数组中搜索和读取数据是很快的。Array获取数据的时间复杂度是O(1),但是要删除数据却是开销很大的,因为这需要重排数组中的所有数据。

  2. 相对于ArrayList,LinkedList插入是更快的。因为LinkedList不像ArrayList一样,不需要改变数组的大小,也不需要在数组装满的时候要将所有的数据重新装入一个新的数组,这是ArrayList最坏的一种情况,时间复杂度是O(n),而LinkedList中插入或删除的时间复杂度仅为O(1)。ArrayList在插入数据时还需要更新索引(除了插入数组的尾部)。

  3. 类似于插入数据,删除数据时,LinkedList也优于ArrayList。

  4. LinkedList需要更多的内存,因为ArrayList的每个索引的位置是实际的数据,而LinkedList中的每个节点中存储的是实际的数据和前后节点的位置。

10.什么情况下用LinkedList或者使用ArrayList

  1. 你的应用不会随机访问数据。因为如果你需要LinkedList中的第n个元素的时候,你需要从第一个元素顺序数到第n个数据,然后读取数据。

  2. 你的应用更多的插入和删除元素,更少的读取数据。因为插入和删除元素不涉及重排数据,所以它要比ArrayList要快。

以上就是关于ArrayList和LinkedList的差别。你需要一个不同步的基于索引的数据访问时,请尽量使用ArrayList。ArrayList很快,也很容易使用。但是要记得要给定一个合适的初始大小,尽可能的减少更改数组的大小

11.Java8中HashMap扩容机制

HashMap是懒加载,只有在第一次put时才会创建数组。
java8是在存入数据后再进行扩容的判断

在这里插入图片描述

JDK1.8主要解决或优化了以下问题:
(1)resize 扩容和 计算hash 优化
(2)引入了红黑树,目的是避免单条链表过长而影响查询效率,红黑树算法请参考
(3)解决了多线程死循环问题,但仍是非线程安全的,多线程时可能会造成数据丢失问题。
扩容会发生在两种情况下(满足任意一种条件即发生扩容):
a 当前存入数据大于阈值即发生扩容
b 存入数据到某一条链表时,此时该链表数据个数大于8,且总数量小于64即发生扩容
链表升级成红黑树的条件
链表长度大于8时才会考虑升级成红黑树,是有一个条件是 HashMap 的 Node 数组长度大于等于64(不满足则会进行一次扩容替代升级)。
红黑树退化成链表的条件
扩容 resize( ) 时,红黑树拆分成的 树的结点数小于等于临界值6个,则退化成链表。 删除元素 remove( ) 时,在 removeTreeNode( ) 方法会检查红黑树是否满足退化条件,与结点数无关。如果红黑树根 root 为空,或者 root 的左子树/右子树为空,root.left.left 根的左子树的左子树为空,都会发生红黑树退化成链表。
HashMap是怎么解决哈希冲突的?
使用链地址法(使用散列表)来链接拥有相同下标的数据; 使用2次扰动函数(hash函数)来降低哈希冲突的概率,使得数据分布更平均; 引入红黑树进一步降低遍历的时间复杂度,使得遍历更快;
HashMap的负载因子默认是多少?
默认初始容量? HashMap 的默认负载因子是 0.75,默认的初始容量为 16。初始容量是指 HashMap 在创建时的初始桶数量。负载因子用于确定何时触发扩容操作。
负载因子 0.75 意味着当 HashMap 中的元素数量达到当前容量的 75% 时,HashMap 将自动进行扩容操作,以保持性能。这是一个平衡的值,通常在性能和内存占用之间提供了合理的折中。
HashMap 的容量总是2的幂,这有助于哈希值与桶的索引之间的关系更加高效。因此,默认初始容量 16 是2的幂。如果以不是2的幂的值创建 HashMap,它将自动向上取最接近的2的幂的值。
插入之前还是之后去检查是否需要扩容?
在 Java 8 中的 HashMap 实现中,插入操作发生后,HashMap 会首先插入新元素到合适的桶中,然后再检查是否需要进行扩容。如果在插入之后元素数量达到或超过了负载因子乘以当前容量,HashMap 就会触发扩容操作。
这种在插入之后进行扩容检查的方式有助于减少插入操作的复杂性,并使 HashMap 在处理插入操作时能够更高效地运行。

链表插入方式?红黑树插入方式?
当发生哈希碰撞时,会根据链表长度或者树结构的情况,采用不同的方式来插入元素:

链表插入方式:
当哈希桶中的键值对数量较少,并且链表长度小于等于8时,新的键值对会被插入到链表的末尾。
这种方式保持了链表的顺序,没有破坏原有的插入顺序,但当链表长度超过一定阈值(8)& 数组长度 > 64时,会触发链表转化为红黑树的操作。
红黑树插入方式:

当链表长度超过了一定阈值(8),HashMap 会将链表转化为红黑树(红黑树是一种自平衡的二叉搜索树)。
新的键值对会按照红黑树的规则插入到红黑树中,以提高检索性能。红黑树的平均检索时间复杂度是 O(log n)。
当红黑树的元素数量减少到一定程度(6),会将红黑树转换回链表,以节省内存。
哈希表的扩容倍数
哈希表的扩容倍数是 2。这意味着当哈希表需要进行扩容时,它会将当前的容量翻倍。这是为了确保哈希表能够保持较低的负载因子,以提高性能。

例如,如果初始容量为 16,当元素数量达到或超过 16 * 0.75 = 12 时,HashMap 将触发扩容操作。在这种情况下,HashMap 会创建一个新的容量为 32 的哈希表,然后重新分配元素到新的桶中。
面试题:为什么数组容量会是2的倍数,以及扩容为什么是扩成两倍?
可以减少碰撞几率,2的倍数 -1 得到值所有位都是1,和计算值相与后能保证结果单一,如果位上的0越多,碰撞概率越大。
比如容量是2的4次方 减1就是 1111.。。那么计算值1110过来和他相与, 结果是1110,另一个计算值1111和他相与结果仍是1111 各自结果不一样 不会碰撞 如果容量不是2的4次方 比如15 减1就是1110. 那么计算值1110过来和他相与, 结果是1110,另一个计算值1111和他相与结果是1110 跟前一个一样 发生碰撞了

面试题:扩容和负载因子之间的关系,元素达到多少扩容2倍,怎么算的?
容量*负载因子 默认长度16的hashmap,负载因子默认0.75所以达到12时会扩容。12代表的是HashMap中元素的总数,即size()方法的返回值

JDK8关于红黑树和链表的知识: 第一次添加元素的时候,默认初期长度为16,当往map中继续添加元素的时候,通过hash值跟数组长度取“与”来决定放在数组的哪个位置,如果出现放在同一个位置的时候,优先以链表的形式存放,在同一个位置的个数又达到了8个(代码是>=7,从0开始,及第8个开始判断是否转化成红黑树),如果数组的长度还小于64的时候,则会扩容数组。如果数组的长度大于等于64的话,才会将该节点的链表转换成树。在扩容完成之后,如果某个节点的是树,同时现在该节点的个数又小于等于6个了,则会将该树转为链表。只有当数据量大于64才会有红黑树+链表。

hashmap底层结构是哈希表(数组下挂链表的节点)
扩容条件、扩容倍数

12.集合不会无限存储数据,最大值:Integer.MAX_VALUE (2147483647)

13.ListIterator:双向输出 ;Enumeration:枚举输出 ;Vector:御用输出

在这里插入图片描述

14.位移运算符

(1)左移 左移运算符“<<” - 使指定值的所有位都左移规定的次数。
左移m<<n 代表把数字m在无溢出的前提下乘以2的n次方。
例如,5<<3 就是5乘以2的3次方,结果是40。
(2)右移  右移运算符“>>” - 使指定值的所有位都右移规定的次数。
右移m>>n 代表把数字m除以2的n次方,原来是正数的还是正数,负数还是负数。
注意,如果是单数,也就是二进制末位为1,则结果是将m除以2的n次方的整数商。
例如:
16>>3 就是16除以2的3次方,结果是2。
15>>3 就是14(15-1)除以2的3次方,结果是1。
(3)无符号右移 无符号右移运算符“>>>” - 同右移,但是结果全变正数。

15.HashMap和HashTable

在这里插入图片描述

forEach输出依存Iterable

16.Objects

1

17.Collections和Collection

(1)Collection和Collections的区别:

Collection是一个集合接口,有许多的类继承与这个接口。
Collections是一个静态的类,有许多快捷的方法可以调用。

(2)collections用法:

创建一个集合(list)
添加元素:Collections.addAll(list,“1”,“2”)
翻转:Collections.reverse(list)
排序:Collections.sort(list) ASCII码
Enumeration枚举,迭代输出例子

18.Stream

Stream.concat(manList.stream().filter(s -> s.length() == 3).limit(3),
                womanList.stream().filter(s -> s.startsWith("林")).skip(1)).map(Actor::new).
                forEach(p -> System.out.println(p.getName()));

这段代码是使用 Java 的 Stream API 对两个不同的字符串列表 manListwomanList 执行一系列操作。它的含义:

  1. manListwomanList 是两个字符串列表,它们包含一些姓名或字符串。

  2. manList.stream()womanList.stream() 将这两个列表转换为流(Stream)以便进行流操作。

  3. .filter(s -> s.length() == 3) 用于筛选流中长度等于 3 的字符串。

  4. .limit(3) 用于限制流中元素的数量为 3 个。

  5. .filter(s -> s.startsWith("林")) 用于筛选流中以 “林” 开头的字符串。

  6. .skip(1) 用于跳过流中的第一个元素。

  7. Stream.concat(...) 用于合并两个流,即将经过过滤和限制操作后的 manList 流和 womanList 流合并为一个流。

  8. .map(Actor::new) 用于将流中的字符串转换为 Actor 对象,通过调用 Actor 类的构造函数来创建对象。

  9. .forEach(p -> System.out.println(p.getName())) 用于遍历流中的每个 Actor 对象,并打印每个对象的名称(假设 Actor 类有一个 getName() 方法用于获取姓名)。

综合起来,这段代码的作用是:从 manList 中选择长度为 3 的前 3 个字符串,以及从 womanList 中选择以 “林” 开头的字符串中排除第一个,然后将这些筛选后的字符串转换为 Actor 对象,最后打印每个 Actor 对象的姓名。这可以用于处理包含人名的两个字符串列表并对它们进行过滤、转换和输出操作。

19.comparable和comparator

内部比较器(Comparable接口)和外部比较器(Comparator接口)_内部比较器和外部比较器的区别-CSDN博客
https://blog.csdn.net/qq_36711757/article/details/80427064

Comparable和Comparator接口都是实现集合中元素的比较、排序的,众所周知,诸如Integer,double等基本数据类型,java可以对他们进行比较,而对于类的比较,需要人工定义比较用到的字段比较逻辑。可以把Comparable理解为内部比较器,而Comparator是外部比较器。
Comparable和Comparator区别(超详细对比分析)_comparator和comparable的区别-CSDN博客
https://blog.csdn.net/qq_45559536/article/details/106091870
(1)Comparable
java.lang包中
Comparable 是一个对象本身就已经支持自比较所需要实现的接口,如String、Integer自己就实现了Comparable接口,可完成比较大小操作。自定义类要在加入list容器中后能够排序,也可以实现Comparable接口,在用Collections类的sort方法排序时若不指定Comparator,那就以自然顺序排序。所谓自然顺序就是实现Comparable接口设定的排序方式。因此我们需要在实现了Comparable接口的类中重写compareTo(T o)方法。 实现了Comparable接口的类的对象的列表或数组可以通过Collections.sort或Arrays.sort进行自动排序。

内部比较器(简单点说就是把比较器写在类的内部)的概念
类实现了Comparable接口,然后重写了compareTo方法(这个方法可以看作比较器),这个类就拥有了内部比较器。注意,你一旦实现了比较器,就说明这个类支持排序,比如单例模式的类就不能进行排序。

下面看看Comparable接口的源码(了解下就行)

package java.lang;
public interface Comparable<T>
{
    public int compareTo(T o); 
1)如果此对象(调用比较器方法的对象)大于指定对象(目标比较对象),返回正整数   
2)如果此对象小于指定对象,返回负整数   
3)如果此对象等于指定对象,返回零 
}

(2)Comparator
java.util包中

Comparator被称为外部比较器,是因为如果我们需要对某个类(为了便于理解,我们这里称这个类为类A)进行排序(该类本身不支持排序),我们可以另外定义一个实现了Comparator接口的类(类B),来作为类A的“比较器”。这个“比较器”只需要实现Comparator接口即可。也就是说,我们可以通过“实现Comparator类来新建一个比较器”,然后通过该比较器对类进行排序。

注意:
1、 类B即实现了Comparator接口的类,一定要实现compare(T o1, T o2)方法,该方法中就是我们自定义的对类A进行比较的规则 ;
2、 int compare(T o1, T o2) 是“比较o1和o2的大小”。返回“负数”,意味着“o1比o2小”;返回“零”,意味着“o1等于o2”;返回“正数”,意味着“o1大于o2”。

外部比较器(简单点说就是把比较器写在类的外边,没错!就是在外边新定义了个比较器类!)的概念
新定义一个类,类名随意,但这个类必须实现Comparator接口,重写compare方法,我们把这个称作外部比较器。

下面看看Comparable接口的源码(了解下就行)

package java.util;

public interface Comparator<T> {  
int compare(T o1, T o2);  
  1)如果o1大于o2,则返回正整数; 
  2)如果o1小于o2,则返回负整数 
  3)如果o1等于o2,则返回零   
boolean equals(Object obj); //你是不是想问为什么这个方法不需要重写?对不起,我也不是特别清楚,但通常是把这个方法忽略掉,如果
必须要个说法,你可以说这个方法被默认实现了。
}

现在我们有两种比较器的实现方法,用的时候用哪种呢?
1.我们自定义一个类时,可以选择内部比较器,内部比较器很符合java封装的思想,也就是高内聚,但是!但是!但是!我们平时用到的类往往不是自定义的,而是别人已经写好并且已编译的类,我们只能调用,不能修改其源代码,这时我们就只能用外部比较器了。

2.还有种情况,我们用到的还是别人已经写好并且已编译的类,他写这个类的时候恰好也实现了内部比较器(我们常用的有基本类型的封装类,String,Date),但是他定义的这种比较方法,不是我们想要的(举个例子,integer的内部比较器是按照数字大小进行比较排序,但是我们的需求是按照数字的绝对值进行排序,这就很尴尬了),这时我们就只能用外部比较器了。

3.第三种情况,我们平时对对象进行排序,往往需要多种排序方式(举个例子,学生表的排序方式有学号排序,年龄排序,性别排序等等),这时我们也不得不用外部比较器了(也就是定义多个外部比较器类)。

由此看出,外部比较器比内部比较器更灵活,更易维护。

比较器的应用
最最常见的应用还是用在集合(list)和数组的sort()方法中,如果我们想用sort()方法,必须实现存储元素对象的内部比较器或者自定义一个用于存储元素对象之间的外部比较器,不然用sort()方法的时候容器会报错,它不知道用哪种方式进行排序。

20.Map

(1)map集合特点
1)map集合特点就是采用了 Key-value键值对映射的方式进行存储 key在Map里面是唯一的但是value可以重复,一个key对应一个value。
2)key是无序、唯一的(不允许重复)
3)value是无序不唯一的(可以重复)
4)Map接口有两个集合HashMap和TreeMap及LinkedHashMap
5)HashMap采用哈希表的存储结构所以里面的数据是无序但是唯一的。(实现唯一的方式就是重写 Hashcode和equals方法)
6)TreeMap采用的是二叉树的存储方式里面的数据是唯一而且有序的而且一般是按升序的方式排列 (要实现comparable接口并且重写compareTo的方法用来实现它的排序)
7)LinkedHashMap是HashMap的进化版它比HashMap速度更快而且还可以保证唯一性和有序性。
8)Map 与 Collection 并列存在。用于保存具有映射关系的数据: Key-Value
9)Map 中的 key 和 value 可以是任何引用类型的数据,会封装到 HashMap$Node 对象中
10)Map 的 key 可以为 null , value 也可以为 null ,注意 key 为 null ,只能有一个,
value为null ,可以多个.
11)常用 String 类作为 Map 的 key
12)key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到对应的 value

(2)Map存储数据的特点是什么?并指明key,value,entry存储数据的特点。
Map存储数据的特点是一个键(key)对应一个值(value),键可以为任意类型但是不可用重复;值可以是任意类型,也可以重复;entry包含一个键值对,以链表的方式存储。
(3)描述HashMap的底层实现原理(jdk8版)
HashMap的底层实现原理是,把一个键值对封装成一个Node对象,再通过Hash函数对键的运算,把键所对应的Node对象放入一个数组内。
当数组的这个位置没有其他Node对象时,就把改Node对象直接放入,当存在其他Node对象时,就以链表的形式,把要存入的node对象往下挂。
在jdk8版及以上版本,当下挂的链表长度超过8时,我们会把改链表转换为红黑树来提升效率。
这样就可以实现HashMap的快速查找。
(4)Map中常用实现类有哪些?各自有什么特点?
Map中常用实现类有HashMap,HashTable,ConcurentHashMap
HashMap:初始容量16,以1.5倍扩容,线程不安全
HashTable:初始容量11,以2倍加一扩容,线程安全的
(5)集合遍历
Map集合是基于java核心类——java.util中的;
Map.Entry 是Map中的一个接口,他的用途是表示一个映射项(里面有Key和Value),而Set<Map.Entry<K,V>>表示一个映射项的Set。
Map.Entry里有相应的getKey和getValue方法,即JavaBean,让我们能够从一个项中取出Key和Value。

Map集合用于储存元素对,Map储存的是一对键值(key和value),是通过key映射到它的value
values() : 是获取集合中的所有的值----没有键,没有对应关系。
KeySet() : 将Map中所有的键存入到set集合中。因为set具备迭代器。所有可以迭代方式取出所有的键,再根据get方法。获取每一个键对应的值。 迭代后只能通过get()取key 。
entrySet():是返回此映射中包含的映射关系的 Set 视图。 Map.Entry表示映射关系,迭代后可以e.getKey(),e.getValue()取key和value。返回的是Entry接口 。

for each遍历
import java.util.Iterator;
import java.util.Map;

public class mapTest {
    public static void main(String[] args) {
        Map<String, String>map = new HashMap<String,String>();
        map.put("student1", "阿伟");
        map.put("student2", "小李");
        map.put("student3", "小张");
        map.put("student4", "小王");
//1.使用map.entrySet().iterator()遍历
        System.out.println("使用entrySet()遍历");
        Iterator it = map.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry entry =(Map.Entry) it.next();
            Object key = entry.getKey();
            Object value = entry.getValue();
            System.out.println("key="+key+"  value"+value);

        }
//2.通过map.Keyset遍历key和value,普遍使用,二次取值
        System.out.println("通过Map.Keyset遍历key和value,普遍使用,二次取值");
        for(String key:map.keySet()){
            System.out.println("Key="+key+"\tvalue="+map.get(key));
        }
//3通过map.values()遍历所有的value,但不能遍历key
        System.out.println("通过map.values()遍历所有的value,但不能遍历key");
        for(String v:map.values()){
            System.out.println("value="+v);
        }
//4通过map.entrySet遍历key和value(推荐使用,特别是容量大时)
//Map.entrySet()方法返回的是一个Set<Map.Entry<K,V>>类型的值,首先该返回值是一个集合Set,集合中的元素是Map.Entry<K,V>类型的,每个Map.Entry可以看作是一个键值对对象,可以通过getKey()和getValue()方法分别获取其键和值。 Map.entrySet()方法主要用在对Map的键和值的遍历上,你可能会问了,既然已经有了更简便的方法去访问Map的键和值,为什么又要弄一个相对复杂的东西呢?答案就是速度更快,特别是在对大容量的Map进行键值对的遍历时。 
        System.out.println("通过map.entrySet遍历key和value(推荐使用,特别是容量大时)");
        for(Map.Entry<String, String>  entry : map.entrySet()){
            System.out.println("key="+entry.getKey()+"\tvalue="+entry.getValue());
        }
 }}

获取的数据的类型:

(6)map.containsKey(key)是否存在key值
(7)根据value值获取键

List<Integer> keyList = new ArrayList();
for(Integer getKey: map.keySet()){
    if(map.get(getKey).equals(country)){
         keyList.add(getKey);
    }
}

(8)Map接口常用方法

put:添加
remove:根据键删除映射关系
get:根据键获取值
size:获取元素个数
isEmpty:判断个数是否为0
clear:清除
containsKey:查找键是否存在

**

21.Iterator和ListIterator之间有什么区别

**
ListIterator继承Iterator:public interface ListIterator extends Iterator

Iterator迭代器包含的方法有:
hasNext():如果迭代器指向位置后面还有元素,则返回 true,否则返回false
next():返回集合中Iterator指向位置后面的元素
remove():删除集合中Iterator指向位置后面的元素

ListIterator迭代器包含的方法有:
add(E e): 将指定的元素插入列表,插入位置为迭代器当前位置之前
hasNext():以正向遍历列表时,如果列表迭代器后面还有元素,则返回 true,否则返回false
hasPrevious():如果以逆向遍历列表,列表迭代器前面还有元素,则返回 true,否则返回false
next():返回列表中ListIterator指向位置后面的元素
nextIndex():返回列表中ListIterator所需位置后面元素的索引
previous():返回列表中ListIterator指向位置前面的元素
previousIndex():返回列表中ListIterator所需位置前面元素的索引
remove():从列表中删除next()或previous()返回的最后一个元素(有点拗口,意思就是对迭代器使用hasNext()方法时,删除ListIterator指向位置后面的元素;当对迭代器使用hasPrevious()方法时,删除ListIterator指向位置前面的元素)
set(E e):从列表中将next()或previous()返回的最后一个元素返回的最后一个元素更改为指定元素e
(1)相同点
都是迭代器,当需要对集合中元素进行遍历不需要干涉其遍历过程时,这两种迭代器都可以使用。

(2)不同点
①使用范围不同,Iterator可以应用于所有的集合,Set、List和Map和这些集合的子类型。而ListIterator只能用于List及其子类型。
②ListIterator有add方法,可以向List中添加对象,而Iterator不能。
③ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。Iterator不可以。
④ListIterator可以定位当前索引的位置,nextIndex()和previousIndex()可以实现。Iterator没有此功能。
⑤都可实现删除操作,但是ListIterator可以实现对象的修改,set()方法可以实现。Iterator仅能遍历,不能修改。

22、list、set、map底层的数据结构

(1)List
Arraylist: Object数组
Vector: Object数组
LinkedList: 双向链表(JDK1.6之前为循环链表,JDK1.7取消了循环)
(2)Set
HashSet(无序,唯一): 基于 HashMap 实现的,底层采用 HashMap 来保存元素
LinkedHashSet: LinkedHashSet 继承与 HashSet,并且其内部是通过 LinkedHashMap 来实现的。有点类似于我们之前说的LinkedHashMap 其内部是基于 Hashmap 实现一样,不过还是有一点点区别的。
TreeSet(有序,唯一): 红黑树(自平衡的排序二叉树。)
(3)Map
HashMap: JDK1.8之前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。JDK1.8以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间
LinkedHashMap: LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。
Hashtable: 数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的
TreeMap: 红黑树(平衡二叉排序树)

Arraylist说明:
底层是数组队列,相当于动态数组。与 Java 中的数组相比,它的容量能动态增长。在添加大量元素前,应用程序可以使用ensureCapacity操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。
它继承于 AbstractList,实现了 List, RandomAccess, Cloneable, java.io.Serializable 这些接口。
数据结构-线性表的顺序存储,插入删除元素的时间复杂度为O(n),求表长以及增加元素,取第 i 元素的时间复杂度为O(1)
ArrayList 继承了AbstractList,实现了List。它是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。
ArrayList 实现了RandomAccess 接口, RandomAccess 是一个标志接口,表明实现这个接口的 List 集合是支持快速随机访问的。在 ArrayList 中,我们即可以通过元素的序号快速获取元素对象,这就是快速访问-随机存取。
ArrayList 实现了Cloneable 接口,即覆盖了函数 clone(),能被克隆。
ArrayList 实现java.io.Serializable 接口,这意味着ArrayList支持序列化,能通过序列化去传输。
和 Vector 不同,ArrayList 中的操作不是线程安全的!所以,建议在单线程中才使用 ArrayList,而在多线程中可以选择 Vector 或者 CopyOnWriteArrayList。

23、底层原理

(1)ArrayList

(1)底层是由动态数组实现的【使用了List接口】。
(2)动态数组是长度不固定,随着数据的增多而变长。
(3)如果不指定,默认长度为10,当添加的元素超过当前数组的长度时,会创建新的数组,新数组长度是当前数组的1.5倍,然后当前数组的元素复制到新的数组后,当前数组的内存被释放。
(4)存储和删除的效率比较低,但是查询的效率非常高。
(5)没有锁,因此是线程不安全的,因此是非同步的。

(2)LinkedList

(1)底层是由双向链表的数据结构实现的【使用了List接口】,
因此存储和删除的效率非常高,但是查询需要从头按顺序找,因此效率比较低。
(2)即便使用了二分查找【分两半查找,先判断index是在链表的哪一半,然后再去对应区域查找,这样最多只要遍历链表的一半节点即可找到】,但速度远比arraylist慢得多
(3)没有锁,因此是线程不安全的,因此是非同步的。

(3)hash table

(1)哈希表,也叫散列表,是根据关键码值直接访问的数据结构。
(2)通过把关键码值映射到表中一个位置来访问记录,以加快查找速度。
(3)那么这个映射函数叫做散列函数,存放记录的数组叫做散列表。

(4)HashMap

(1)键值都允许为null,是哈希表的Map接口实现,没有锁,因此是线程不安全的,是非同步的。
(2)底层是使用动态数组【使用Node[] ,不使用Entry[]存储键值对】和单向链表 组合实现的,也就是说,底层是节点数组,每个数组元素内部装有单向链表,
当链表长度大于一定值时,链表会转换成红黑树【jdk1.8后才有红黑树】,这样可以减少链表查询时间。
(3)动态的节点数组在扩容时,会新建一个数组,并把当前数据元素赋值到新数组中,这个过程很耗性能。
(4)存入数据时【也就是要存入一个Node对象】,会根据key的hash算法来决定存在数组
【Node[]】中的哪个位置,在该位置上【也就是在这个数组元素上】,再根据equals方法来决定存储在该位置链表的哪个位置。
(5)读取数据时【也就是取出一个Node对象】,会根据key的hash算法来找到其在数组中的存储位置,然后使用equals方法,从该位置的链表中找到该Node。

(5)Hashtable

(1)键值都不允许为null,是哈希表的Map接口实现,线程安全,因为使用了同步锁synchronized,因此每次线程操作表时,会加锁,其他线程需要等待。
(2)底层是使用动态数组【Entry[],即键值对对象数组】和单向链表 组合实现的【与HashMap类似】,
也就是说,底层是Entry数组,每个数组元素内部装有单向链表。
(3)【存储数据和读取数据与HashMap十分类似】
存入数据时【也就是要存入一个Entry对象】,会根据key的hash算法来决定存在数组
【Entry[]】中的哪个位置,在该位置上【也就是在这个数组元素上】,再根据equals方法来决定存储在该位置链表的哪个位置。
读取数据时【也就是取出一个Entry对象】,会根据key的hash算法来找到其在数组中的存储位置,然后使用equals方法,从该位置的链表中找到该Entry。
(4)总体来说,与HashMap十分相似,区别在于线程是否安全、键值是否允许为null、
底层数组类型具体是什么、链表是否会转换成红黑树。
(5)注意Hashtable与hash table 单词写法,两个不是一个东西。

(6)ConcurrentHashMap

(1)是线程安全的,内部分多个段,每个段其实就是个小的Hashtable也就是说,
每次线程操作段都要加锁,但是不同段互不影响,因此,如果是多线程操作不同的段,
则允许并发操作,此外,允许并发查询同一个段的内容,查询不需要加锁,
如果是对段做写操作则会加锁,线程同步阻塞。
(2)因为段使用了Hashtable,因此底层数组类型也是Entry[],具体的底部存储和读取实现也是与Hashtable相同。

(7)hash code

(1)译为 哈希值 ,
(2)对象  相等则  哈希值  一定相等, 
(3)但 哈希值 相等,对象 不一定相等。

(8)HashSet

(1)意为哈希集合,HashSet实现 Set接口 。
(2)底层是使用HashMap来存储数据的,各个操作实际上是调用了HashMap的方法来完成,因此HashSet元素是无序的。
(3)存储在HashSet的元素,实际上,将会存在HashMap的key里面,而值为一个固定的值【是什么无关紧要,但是必须统一固定】,每次添加新元素时,
  将会调用HashMap的put()方法,既然值时固定的,那么如果key已经存在,则HashMap不会再新建重复的元素,这就是HashSet存储数据不可重复的原因。

(9)LinkedHashMap

(1)继承于HashMap,因此有HashMap的特点,非同步、允许键值为null等。
(2)通过重写HashMap相关方法,重新定义了数组中保存的元素Entry,每个元素除了保存当前对象的引用外,还保存了其上一个元素before和下一个元素after的引用,从而在哈希表的基础上又构成了双向链接列表。

(10)LinkedHashSet

(1)意为 线性哈希集合。
(2)LinkedHashSet底层使用LinkedHashMap来保存所有元素。
(3)继承了HashSet,其所有的方法操作上又与HashSet相同。

24、集合顺序

List(有序)、数组:
顺序连续存储,顺序输出

HashMap(无序Map):
HashMap不保证插入顺序,但是循环遍历时:以Integer或String作为数据类型时,数据量小时输出顺序不会改变的,数据多了就不是有序的,其他类型作为key,也不是有序的。

TreeMap(有序Map):
存放数据按照键排序存,输出的是排序后的结果
TreeMap 实现了 SortedMap 接口,也就是说会按照 key 的大小顺序对 Map 中的元素进行排序,key 大小的评判可以通过其本身的自然顺序(Comparable),也可以通过构造时传入的比较器(Comparator)。

HashTable(无序Map):
存放元素无序

HashSet(无序Set):
Set不能有重复的元素(正确重写equals方法和hashCode方法,以保证放入Set对象的唯一性),HashMap不允许有重复的键

HashSet没有提供get()方法,愿意是同HashMap一样,Set内部是无序的,只能通过迭代的方式获得

在HashSet中,元素都存到HashMap键值对的Key上面,而Value时有一个统一的值private static final Object PRESENT = new Object();,(定义一个虚拟的Object对象作为HashMap的value,将此对象定义为static final。)

TreeSet(有序Set):
TreeSet的底层实现是TreeMap(TreeMap的底层实现借助了HashMap)

TreeSet中存放的元素是有序的(不是插入时的顺序,是有按关键字大小排序的),且元素不能重复。

实现有序存储:需要有一个比较器,其实说起来,TreeSet更受关注的是不重复且有序,这个有序就需要有一个compare的过程,因此会需要参数实现Comparable接口。

set集合没有提供get()方法,要用迭代器来获取想要的元素

LinkedHashSet(有序Set)
链表存储数据,有序

四类存储结构:顺序存储、链接存储、索引存储 和 散列存储。

顺序结构和链接结构适用在内存结构中。
索引结构和散列结构适用在外存与内存交互结构。
顺序存储:
在计算机中用一组地址连续的存储单元依次存储线性表的各个数据元素,称作线性表的顺序存储结构。

特点:

1、随机存取表中元素。

2、插入和删除操作需要移动元素。

链接存储:
在计算机中用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的)。它不要求逻辑上相邻的元素在物理位置上也相邻.因此它没有顺序存储结构所具有的弱点,但也同时失去了顺序表可随机存取的优点。

特点:

1、比顺序存储结构的存储密度小 (每个节点都由数据域和指针域组成,所以相同空间内假设全存满的话顺序比链式存储更多)。
2、逻辑上相邻的节点物理上不必相邻。
3、插入、删除灵活 (不必移动节点,只要改变节点中的指针)。
4、查找结点时链式存储要比顺序存储慢。
5、每个结点是由数据域和指针域组成。

索引存储:
除建立存储结点信息外,还建立附加的索引表来标识结点的地址。索引表由若干索引项组成。

特点:

索引存储结构是用结点的索引号来确定结点存储地址,其优点是检索速度快,缺点是增加了附加的索引表,会占用较多的存储空间。

散列存储:
散列存储,又称hash存储,是一种力图将数据元素的存储位置与关键码之间建立确定对应关系的查找技术。

散列法存储的基本思想是:由节点的关键码值决定节点的存储地址。散列技术除了可以用于查找外,还可以用于存储。

特点:

散列是数组存储方式的一种发展,相比数组,散列的数据访问速度要高于数组,因为可以依据存储数据的部分内容找到数据在数组中的存储位置,进而能够快速实现数据的访问,理想的散列访问速度是非常迅速的,而不像在数组中的遍历过程,采用存储数组中内容的部分元素作为映射函数的输入,映射函数的输出就是存储数据的位置,这样的访问速度就省去了遍历数组的实现,因此时间复杂度可以认为为O(1),而数组遍历的时间复杂度为O(n)。

25、list、set、map的遍历方式

①List:

1)for循环遍历(需要获取index的情况)
	for(int i=0;i<list.size();i++){
    		list.add(i);
	}
  2)增强for循环遍历
	for(Integer integer : integers){
   		System.out.println(integer);
	}
  3)iterator遍历
	Iterator<String> it = list.iterator();
		while(it.hasNext()){
   		 String x = it.next();
   		 System.out.println(x);
	}

②Set:

1)增强for循环遍历
        for (String s:sets){
            System.out.println(s);
        }
  2)迭代器遍历
        Iterator<String> iterators = sets.iterator();
        while (iterators.hasNext()) {
            String value= iterators.next();
            System.out.println(value);
        }
  3)集合类通用的遍历方式(早期就存在)
        for (Iterator it2 = sets.iterator();it2.hasNext();){
            System.out.println(it2.next());
        }
  4)jdk1.8之后
        sets.forEach(System.out::println);

③Map:

1)增强for循环遍历(使用较多)
        for(String s:maps.keySet()) {
            System.out.println(s+":"+maps.get(s));
        }
2)迭代器遍历(根据entrySet来遍历):调用entrySet方法将Map集合转换成Set集合,     	 Set集合中每个元素都是键值对实体类型,然后遍历Set集合,提前键和值。
        Iterator<Map.Entry<String, String>> entries = maps.entrySet().iterator();
        while (entries.hasNext()){
            Map.Entry<String,String> map = entries.next();
            System.out.println(map.getKey()+":"+map.getValue());
        }
 3)迭代器遍历(根据keySet来遍历)
        Set<String> names = maps.keySet();
        Iterator<String> name = names.iterator();
        while(name.hasNext()) {
            String key = name.next();
            System.out.println(key +":"+maps.get(key));
        }
 4)增强for结合Map.Entry(容量大时使用)
        for(Map.Entry<String, String> s:maps.entrySet()){
            System.out.println(s.getKey()+":"+s.getValue());
        }
5)Lambda表达式遍历:Lambda中foreach的本质还是先将Map集合转成entrySet,然后进行遍历返回
	Map<String, String> maps = new HashMap<>();
	maps.put("person1", "168");
	maps.put("person2", "160");
	maps.put("person3", "172");
	maps.put("person4", "170");
	maps.forEach((key, value) -> System.out.println("key:" + key + ",value:" + value));

26、java常用集合的初始容量+扩容倍数

StringBuilder

初始容量16
扩容乘2+2
线程不安全
可变

StringBuffer

初始容量16
扩容乘2+2
线程安全
可变

ArrayList

初始容量为10
扩容为原来的容量的1.5倍
jdk7是一开始就创建了初始为10的数组,jdk8是调用add方法时,才创建容量为10的数组
线程不安全
底层是数组

Vector

初始容量为10
扩容为原来的容量的2倍
线程安全
加载因子为1

ArrayList和Vector的区别:

Vector和ArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于强同步类。因此开销就比ArrayList要大,访问要慢。正常情况下,大多数的Java程序员使用ArrayList而不是Vector,因为同步完全可以由程序员自己来控制。Vector每次扩容请求其大小的2倍空间,而ArrayList是1.5倍。Vector还有一个子类Stack。

HashMap

Hashmap的默认容量是:16
负载因子为:0.75
扩容为原来的两倍
线程不安全
jdk7是一开就创建容量为16,jdk8是先创建{}当调用put方法时才初始化容量为16
jdk7是数组+链表 jdk8是数组+链表+红黑树
jdk7的节点是entry ,jdk8节点是node
jdk7是头插法,jdk8是尾插法 七上八下
当链表长度大于8就转化成红黑树

Hashtable

Hashtable 初始容量为:11
负载因子为:0.75
扩容为原来的两倍+1
线程安全

HashMap和Hashtable的区别:

(1)Hashtable 是线程安全的,HashMap 不是线程安全的。
(2)Hashtable 所有的元素操作都是 synchronized 修饰的,而 HashMap 并没有。
既然 Hashtable 是线程安全的,每个方法都要阻塞其他线程,所以 Hashtable 性能较差,HashMap 性能较好,使用更广。如果要线程安全又要保证性能,建议使用 JUC 包下的 ConcurrentHashMap
(3)Hashtable 是不允许键或值为 null 的,HashMap 的键值则都可以为 null。
(4)两者继承的类不一样,Hashtable 继承了 Dictionary类,而 HashMap 继承的是 AbstractMap类。
(5)HashMap 的初始容量为:16,Hashtable 初始容量为:11,两者的负载因子默认都是:0.75。
(6)HashMap扩容为原来的两倍,Hashtable扩容为原来两倍+1

HashSet

初始容量为16
加载因子为0.75
扩容为原来的两倍
线程不安全
底层其实是HashMap

TreeMap 和 TreeSet

TreeMap的底层采用红黑树的实现。底层数据结构确实是树结构,但它们不需要像某些其他数据结构(如哈希表)那样进行显式的扩容操作。这是因为树结构具有自平衡性质,通过旋转和重新着色节点来保持平衡。

具体来说,TreeMap 和 TreeSet 使用的底层数据结构是红黑树(Red-Black Tree),这是一种自平衡的二叉搜索树。当你向 TreeMap 或 TreeSet 插入元素时,红黑树会自动进行平衡,以保持其红黑树性质。这包括树的高度自动调整,确保平均情况下插入、删除和查找操作的性能保持在 O(log n) 的水平,其中 n 是树中的节点数。

因此,TreeMap 和 TreeSet 不需要显式的扩容操作,因为它们的底层数据结构能够自我调整以应对不断增长的数据。这使得它们能够高效地处理有序数据集合,而不需要额外的扩容逻辑。

27、字节序列

1、Java的字节序列是指将Java对象转换成一串由二进制字节组成的数组,通常通过序列化技术来实现。这个过程被称为对象序列化。序列化允许你将Java对象转换成字节流,以便可以将它们保存到磁盘上,传输到其他计算机,或者在网络上进行传输。

2、对象序列化的主要目的是实现对象持久化,也就是将对象的状态保存下来,以便在需要的时候可以重新加载和使用这些对象。这在分布式系统、数据存储和通信中非常有用,因为它允许对象在不同的系统之间进行交换和传输。

3、Java提供了一些类和接口来支持对象序列化,最常见的是java.io.Serializable接口。通过实现这个接口,你可以指定哪些对象可以被序列化。另外,Java还提供了ObjectOutputStream和ObjectInputStream等类,用于将对象序列化为字节流和将字节流反序列化为对象。

总之,Java的字节序列是一种用于将Java对象转换成二进制数据以进行持久化或传输的机制。这允许你在不同的环境中轻松地保存和恢复对象的状态。

  • 23
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值