集合(数组、链表、map)

目录

Collection包结构 和collections区别

List

数组和arrayList 区别

数组下标为什么从0开始?

ArrayList 动态数组

LinkedList双向链表增删快

增删快

链表

单链表和双链表区别

Arraylist VS LinkedList 区别

数组和List之间转换

ArrayList 、LinkedList、HashMap查找元素时间复杂度

Map 

HashMap

Hash算法解决冲突

Put

Get

resize()

计算hash值

map四种便利

数组和MAP访问数据谁更快

HashMap 1. 7 VS 1.8不安全体现

hashMap /hashTable/concurrentHashMap区别

ConcurrentHashMap

HashTable(全表锁)VS ConcurrentHashMap

HashMap VS TreeMap

HashMap和HashTable区别

HasmMap和HashSet的区别

weakHashMap

TreeMap

LinkedHashMap 


Collection包结构 和collections区别

Collection是集合类的上级接口,

子接口有 Set(无序、不能重复)、

List(有序、可重复)、map(key-value)、

LinkedList、ArrayList、Vector、Stack(先进后出)、queue。

Collections是集合类的一个帮助类,有各种有关集合操作的静态多态方法,

用于实现对各种集合的搜索、排序、线程安全化等操作。

此类不能实例化,就像一个工具类,服务于Java的Collection框架。

List有序

集合

结构

安全

方法

ArrayList

动态数组 10

LinkedList

双向链表

Vector

动态数组  10

synchronized

Stack

stack继承vector

后进先出的堆栈

push(压入)

pop(弹出)

peek(得到栈顶元素)

empty(判空)search(检测一个元素在堆栈中的位置)

Set

无序

去重

HashSet

TreeSet

TreeSet是SortedSet接口的实现类,可以保证元素处于排序状态,采用红黑树的数据结构存储集合元素

LinkedHashSet

是HashSet的子类,具有HashSet的特性,它使用链表维护元素的添加顺序,因此性能低于HashSet,便于迭代访问

EnumSet

专为枚举类设计的集合类,不允许添加null,集合元素有序

Queue

消息阻塞队列

Deque

双端队列

支持在两端插入和移除元素

PriorityQueue

优先队列

优先级堆的无界优先级队列;

优先级队列不允许使用 null 元素

Map

无序

<K,V>的存储

HashMap

Node数组

+ 单链表结构(链表是解决hash冲突的 拉链法)

+红黑树

当链表达到8,数组到达64转为红黑树

TreeMap

SortedMap的是实现类,与TreeSet一样使用红黑树的数据结构,每个<k,v>作为红黑树的一个节点,通过k对节点进行排序

HashTable

Synchronized

(全表锁)

Concurrent

HashMap

Cas+node数组synchronize

Synchronized

分段锁(Java 5)CAS (Java 8)

Properties

是HashTable的子类,它是一个K和V都是String类型的Map,主要用于配置文件的读取

Linked

HashMap

是HashMap的子类,使用双向链表来维护Key-Value对插入的次序,在迭代访问时有较好的性能

weakHashMap

它是一个“弱键”,它的Key值和Value都可以是null,而且其Map中如果这个Key值指向的对象没被使用,此时触发了GC,该对象就会被回收掉的。

线程安全

Vector、HashTable、StringBuffer

List

List 有序集合,可重复,提供按索引方式访问,继承Collection。

List有两个重要的实现类 ArrayList、LinkedList。Vector是线程安全的

ArrayList的toArray方法返回一个数组。

ArrayList的AsList方法返回一个列表。

ArrayList底层实现是Array,数组扩容实现。System.arraycopy()

LikedList是一个双链表,在添加和删除元素时具有比ArrayList更好的性能,

get、set方面弱于ArrayList, 这些对比都是指数据量很大或者操作频繁的情况。

Vector是同步的,add/remove/sort/subList方法都加synchronized

数组和arrayList 区别

空间大小

  1. Array的空间大小是固定的

空间不够时也不能再次申请,所以需要事前确定合适的空间大小。

  1. ArrayList的空间是动态增长的

如果空间不够,它会创建一个空间比原空间大1.5倍的新数组,

然后将所有元素复制到新数组中,接着抛弃旧数组。

而且,每次添加新的元素的时候都会检查内部数组的空间是否足够。

扩容 Arrays.copyOf()

优点:Array获取数据的时间复杂度是O(1),但是要删除数据确实开销很大,

因为这需要重排数组中所有数组(因为删除数据以后,需要吧后面所有的数据前移,重新搞一个数组)。

缺点:数组初始化必须指定初始化长度,否则报错。

public static native void Arrays.arraycopy(Object src, int srcPos, Object dest, int destPos,   int length);

src - 源数组。

srcPos - 源数组中的起始位置。

dest - 目标数组。

destPos - 目的地数据中的起始位置。

length - 要复制的源数组元素的数量。

LinkedList 的双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,

分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,

都可以很方便地访问它的前驱结点和后继结点。

https://juejin.cn/post/6956841940948090887

例如:

数组下标为什么从0开始?

历史和语言设计传统

Java 语言在设计时受到了 C 和 C++ 语言的影响。

C 语言数组下标就是从 0 开始的,Java 继承了这种设计方式,

以保持和底层系统编程以及前辈语言的兼容性,方便熟悉这些语言的程序员学习和使用 Java。

内存地址计算

在 Java 中,数组是一块连续的内存空间。数组元素的内存地址计算方式和下标紧密相关。

对于一个数组arr,计算元素arr[i]的内存地址时,

是通过数组的首地址加上i乘以单个元素占用的字节数来得到的。

当从 0 开始计数时,第一个元素的偏移量为 0,

这种计算方式简单直接,方便编译器和 JVM(Java 虚拟机)高效地定位元素

寻址公式是:baseAddress +i *dataTypeSize, 计算下标的内存地址快

baseAddress 首地址 dataTypeSize 该类型宽度(占用字节数)

https://www.cnblogs.com/xixixing/p/17783149.html

与数据结构和算法的契合

许多数据结构和算法在实现和描述过程中,基于 0 开始计数更自然。

比如在循环遍历数组、进行二分查找等操作时,从 0 开始计数能更好地和数学模型及算法逻辑相匹配,

减少计算中的调整和转换步骤。

ArrayList 动态数组

连续内存存储,适合下标访问

使用尾插法【时间复杂为O(1】并指定初始化容量可以极大提升性能,

甚至超过linkedList(需要创建大量的node对象)

优点:ArrayList查询快随机访问性强

底层是用的数组存储,数组的查询实际上是对引用地址的访问,不需要遍历。

例如:new int arr[5];

缺点:增删慢(使用数组的弊端)

  1. 每当头插、中间插入和删除操作时需要搬运数据(向前或向后的移动元素),时间复杂度是O(N)
    2)  数组需要连续的内存空间,对空间要求高
    3)  数组是固定大小的,但是ArrayList 插入元素会触发扩容机制

缩容时机
如果当前数组大小大于数组初识容量(比如初识容量为10,当添加第11个元素时)就会进行扩容,

新的容量为旧的容量的1.5倍
容方式
会以新的容量创建一个原数组的拷贝,将原数组的数据拷贝过来,原数组会被GC回收。

LinkedList双向链表增删快

基于链表,存储在分散的内存中,适合做数据的插入及删除操作,不适合查询,

需要逐一遍历;遍历linkedList必须使用iteator不能使用for循环

因为每次for循环体get(i)取得某一元素时都需要对list重新进行遍历,性能消耗极大。

另外不要试图使用indexOf等放回元素索引,并利用其遍历,

使用indexOf对list进行遍历,当结果为空时会遍历整个列表。

  • LinkedList中维护了两个属性:一个是first和last分别指向首节点和尾节点
  • 每一个节点里面有一个Node对象,里面有维护了prev,next,item三个属性,

其中通过prev指向指向前一个节点,通过next指向后一个节点。最终实现双向链表

LinkedList 和链表的区别

给定一个单向链表,删除最后一个节点,时间复杂度是O(N)
给定一个LinkedList, 删除最后一个节点,时间复杂度为O(1)

优点:

1.内存利用率高,不会浪费内存
2.大小不固定,拓展灵活
3.插入、删除头尾速度快,时间复杂度为O(1)

缺点:

LinkedList 中间位置插入元素,时间复杂度为O(N),且不能随机查找,必须遍历链表

移除元素时候要用iterator 迭代器,如果for 会报错,因为ConcurrentModificationException

 [ˌmɒdɪfɪˈkeɪʃn]

for loop(从后往前)可以

增强for循环(即foreach循环)其实就是根据list对象创建一个iterator迭代对象,

用这个迭代对象来遍历list,相当于list对象中元素的遍历托管给了iterator,

如果要对list进行增删操作,都必须经过iterator。

List中modCount就会变化,如果这个modCount和exceptedModCount不一致,就会抛出异常。

增删快

原因:双向链表的数据结构,只需要修改插入位置或删除位置左右数据的引用目标即可。

链表

链表是一种物理存储单元上非连续、非顺序的存储结构

数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。  

插入O(1),查找节点、访问特定编号节点O(n)

单链表:【data,next】二元组数组,下一个结点地址

N个结点依次相连构成链表,称为线性表的链式存储表示,

链表每个结点只包含一个指针域,故有称为线性链表。最后一个结点值是否为NULL。

双链表双向链表其实是单链表的改进

【prev,data,next】

当我们对单链表进行操作时,有时你要对结点直接前驱操作时,又必须从头开始查找。

这是由单链表的结构所限制。因为单链表每个结点只有一个存储直接后继结点地址的链域,

那能不能定义一个既有存储后续结点地址的链表域又有存储直接前驱结点的地址链域

这样一个双链域结构的结构呢?这就是双向链表

双向链表中,结点除含有数据域外,还有连个链域,一个存储直接后续结点地址,

一般称为右链域,一个存储直接前驱结点地址,一般称之为左链域。

循环链表循环链表是与单链表一样,是一种链式的存储结构,所不同的是,

循环链表的最后一个结点的指针是指向该循环链表的第一个结点或者表头结点,

从而构成一个环形的链。

单链表和双链表区别
  • 单链表只有一个方向,结点只有一个后继指针
  • 双链表它支持两个方向,每个结点不止有一个后继指针next指向后面的节点,

还有个一个前驱prev指向前面的节点

java中的List接口(ArrayList、Vector、LinkedList) - 平凡晨 - 博客园

Arraylist VS LinkedList 区别

arrayList

linkedList

vector

默认容量

10

双链表存储方式,对容量没有要求,不需要扩容

10

数据结构

动态数组 

双向链表

动态数组

扩容

原来1.5倍

插入更方便,随用随取

原来容的两倍

查询

下标查找O(1)

遍历查找值0(n)

遍历查找0(n)

O(1)

插入

尾部插入O(1)

头/中间插入O(n)数据移动

头插法O(1)

尾插法O(n) 遍历

修改

下标O(1)

值O(n)遍历

LinkedList没有下标O(n)

删除

O(n)数据移动;

最后一个元素,时间复杂度为O(1)

/尾节点O(1)

其他O(n) 遍历

线程安全

synchronized

场景

通过下标直接查找/修改

添加/删除元素

不用移动数据

1.底层结构的不同

ArrayList动态数组来存储元素

同时数组也属于顺序结构的线性表,逻辑上和物理上都是连续的。

Transient Object[] elementData;

LinkedList双向链表来存储元素; 可当作堆栈、队列、双端队列使用,逻辑上是连续的,

但是在物理(真实的内存上)上不一定连续。

2.操作数据效率

①ArrayList按照下标查询时间复杂度O(1)[内存是连续的,根据寻址公式],

LinkedList不支持下标查询。

②未知索引查找:都需要遍历时间复杂度O(n)

③新增和删除

    ArrayList尾部插入和删除,时间复杂度O(1);其他部分增删需要挪动数组,时间复杂度O(N)

      LinkedList头尾节点增删时间复杂度O(1); 其他都需要遍历表,时间复杂度O(N);

3.空间占用

    ArrayList底层是数组,内存连续,节省空间。

    LinkedList 是双向链表需要存储数据,两个指针,更占内存空间。

4.线程都不是线程安全的

   保证线程安全:在方法内使用,局部变量则是线程安全的

                 使用Collections.synchronizedList(new ArrayList()/new LinkedList)加锁。

原文链接:https://blog.csdn.net/wlm123666/article/details/117845615

数组和List之间转换

//数组转list对象引用
List<String> list = Arrays.asList("1", "3", "7", "5");

数组转list,数组内容修改了,list也会改变,因为底层:对象引用,没有创建新的数组。
//list转数组数组拷贝不会有影响
String[] objects = list.toArray( new Strng[list.size() ]);
for (String o : objects) {
    System.out.println(o);
}

ArrayList 、LinkedList、HashMap查找元素时间复杂度

ArrayList 根据下标O(1), 根据值查找O(N),

LinkedList查找值的复杂度O(N),

HashMap 哈希函数不冲突O(1) ,冲突最坏情况O(n)

ArrayList 和 LinkedList 是 Java 中常用的两种线性数据结构,它们在查找操作上的时间复杂度有所不同。

ArrayList 是基于数组实现的动态列表,在查找元素时,时间复杂度为 O (1) 或 O (n)

这取决于查找方式。如果是通过索引直接访问元素,由于数组在内存中是连续存储的,

计算机可以通过简单的地址计算直接定位到指定索引的元素,所以时间复杂度为 O (1)

但如果是按值查找元素,需要遍历数组中的元素,从第一个元素开始逐个比较,

直到找到目标元素或遍历完整个数组,这种情况下平均时间复杂度为 O (n)n 为数组中元素的个数

寻址公式是:baseAddress + i *dataTypeSize, 计算下标的内存地址。

baseAddress 首地址 dataTypeSize 该类型宽度(占用字节数)

LinkedList 是基于链表实现的列表,查找元素时时间复杂度为 O (n)

无论是通过索引查找还是按值查找,都需要从链表的头节点(或尾节点,取决于查找方向)

开始逐个遍历节点。因为链表中的节点在内存中不是连续分布的,

不能像数组那样直接通过地址计算定位元素,

所以每次查找都需要沿着链表的指针依次访问下一个节点,

直到找到目标元素或遍历完整个链表,因此时间复杂度为 O (n)。

哈希表查找值的时间复杂度在理想情况下为 O (1),但在最坏情况下会退化为 O (n)

在理想状况下,哈希函数能将每个键均匀地映射到哈希表的各个槽中,没有冲突

当查找某个值时,通过哈希函数计算键的哈希值,直接定位到对应的槽,

就能马上找到值,这个过程的时间复杂度是常数级别的 O (1)

然而,实际应用中,由于哈希表的存储空间有限不同的键可能会映射到相同的槽,即产生冲突

当冲突解决策略不够好时,比如采用简单的线性探测法,随着插入元素的增多,

冲突会越来越严重,可能会形成很长的冲突链。在这种最坏的情况下

查找一个值就需要遍历冲突链上的所有元素,时间复杂度会退化为与元素个数 n 相关的 O (n)

不过,通过合理选择哈希函数、调整哈希表的大小和采用高效的冲突解决策略,

可以将哈希表查找的时间复杂度尽可能地接近理想情况的 O (1)。

Map 

HashTable /LinkedHashMap /TreeMap  /ConcurrentHashMap/weakHashMap/EnumMap键是枚举类型

HashMap
  1. Key-value 数据结构,存取无序,键和值都可以为null,键是唯一的

2. 是Node数组+链表[链表是解决hash冲突的 拉链法]/红黑树(JDK1.8增加了红黑树部分);

优化的部分就是把链表结构变成了红黑树。原来jdk1.7的优点是增删效率高,

于是在jdk1.8的时候,不仅仅增删效率高,而且查找效率也提升了。从链表的o(n)降低为o(logn)。

3.非线程安全,默认容量为16,加载因子默认阈值0.75

4.扩容2的N次幂 (16,32,48,64,128,256)

查询时间复杂度数组o(1),链表o(n),红黑树 o(logn)

JDK1.8,链表的深度>8数组容量大于64时,扩容的时候会把链表转成红黑树

当红黑树的节点深度小于等于6时,红黑树会转为链表结构。

【1亿个出现6个的概率转红黑树】

树的特点:短(深度)、旋转、查询快

Hash算法解决冲突

开放定址法: 一旦发生了冲突,就去寻找下一个空的散列地址。

再哈希法: 多个不同的Hash函数,当发生冲突时,使用第二个,第三个,…。

建立公共溢出区: 将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表

www.cnblogs.com/lyfstorm/p/11044468.html

目前流行的 Hash 算法包括 MD5、SHA-1 和 SHA-2

信息安全:文件校验、数字签名、鉴权协议

Put
  1. 对key做hash
  2. Put时若数组为空则创建一个新数组 resize()扩容
  3. 根据hash算法计算在数组的存在位置是否冲突,

不冲突直接插入newNode(hash, key, value, null);插入到数组中即可。

  1. 冲突的话,判断数组中的元素是否与插入的key一样,若相同直接替换旧值。
  2. 判断插入数据结构是链表还是红黑树;

若是红黑树putTreeVal(),若是链表便利数组是否存在,不存在直接newNode(hash, key, value, null).

(jdk1.8尾部插入新的节点,1.7是头插)。

注意一点:不存在并且在链表末尾插入元素的时候,会判断binCount >= TREEIFY_THRESHOLD - 1。

也就是判断当前链表的长度是否大于阈值8,如果大于那就会把当前链表转变成红黑树,

方法是而且数组的长度不小于64,treeifyBin(tab, hash)而又当小于6时,则转化为链表存储。

Get
  1. 获取key的hash值。
  2. 从链表的首节点开始查询,如果key相同就返回。
  3. 遍历红黑树/链表有相同的返回,没有返回null。
resize()

Threshold=数组长度*0.75  /ˈθreʃhəʊld/

大于threshold扩容

  1. 排查异常情况  整数最小范围<容量<整数最大范围
  2. 扩容2倍新建数组
  3. 把旧数组的数据移动到新数组中

计算hash值

取key的hashCode值、高位运算、取模运算。

方法一:

static final int hash(Object key) {   //jdk1.8 & jdk1.7

     int h;

     // h = key.hashCode() 为第一步 取hashCode值

     // h ^ (h >>> 16)  为第二步 高位参与运算

     return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

}

方法二:

static int indexFor(int h, int length) {  //jdk1.7的源码,jdk1.8没有这个方法,但是实现原理一样的

     return h & (length-1);  //第三步 取模运算

}

参考:

HashMap实现原理、源码解析(jdk1.8) - 简书

https://zhuanlan.zhihu.com/p/79219960

map四种便利

public static void main(String[] args) {

    Map<String, String> map = new HashMap<String, String>();

    map.put("1", "value1");

    map.put("2", "value2");

    map.put("3", "value3");

  

    //第一种:普遍使用,二次取值

    System.out.println("通过Map.keySet遍历key和value:");

    for (String key : map.keySet()) {

        System.out.println("key= "+ key + " and value= " + map.get(key));

    }

  

    //第二种

    System.out.println("通过Map.entrySet使用iterator遍历key和value:");

    Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();

    while (it.hasNext()) {

        Map.Entry<String, String> entry = it.next();

        System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());

    }

  

    //第三种:推荐,尤其是容量大时

    System.out.println("通过Map.entrySet遍历key和value");

    for (Map.Entry<String, String> entry : map.entrySet()) {

        System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());

    }

    //第四种

    System.out.println("通过Map.values()遍历所有的value,但不能遍历key");

    for (String v : map.values()) {

        System.out.println("value= " + v);

    }

 }

lambda表达式forEach遍历

map.forEach((k, v) -> System.out.println("key = " + k + ", value = " + v));

HashMap和ConcurrentHashMap的知识总结 - 简书

ConcurrentHashMap底层详解(图解扩容)(JDK1.8)_concurrenthashmap原理1.8-CSDN博客

List、Set和Map接口的特点与常用的实现类 - Java面试题

https://blog.csdn.net/q5706503/article/details/85122343

数组MAP访问数据谁更快

若是比较某个固定元素的访问速度,那是数组快

若是随机访问,自然是BTREE的map快。

hashMap 存100个键值对,初始化高,应该设为多少

HashMap的实例有两个参数影响其性能:初始容量和加载因子 0.75。

128*0.75=96,256*0.75=192结果256

指定容量大小:2的n次方(幂)。

HashMap 1. 7 VS 1.8不安全体现

1.7 node数组加单链表,1.8后链表冲突多了红黑树;

1.7中新增节点采用头插法,

1.8中新增节点采用尾插法(避免逆序且链表死循环的问题)

让HashMap线程安全 Map m = Collections.synchronizeMap(hashMap);

原因

jdk1.7HashMap线程不安全主要是发生在扩容函数中

其中调用了HshMap的transfer()。方法在执行数组扩容操作时,数据会重新定位新数组中索引,

并采用头插法将元素迁移到新数组中。头插法会将链表的顺序翻转,

这也是形成死循环的关键点。如何造成死循环以及数据丢失的。

改善:

数据丢失、死循环已经在在JDK1.8中已经得到了很好的解决,

1.8的源码会发现找不到HashMap#transfer(),

JDK1.8直接在HashMap#resize()中完成了数据迁移。

HashMap线程不安全的体现:

JDK1.7 HashMap线程不安全体现在:死循环、数据丢失  

JDK1.8 HashMap线程不安全体现在:数据覆盖

hashMap /hashTable/concurrentHashMap区别

为什么HashMap线程不安全?以及实现HashMap线程安全的解决方案_hashmap线程安全吗 什么解决方案-CSDN博客

多线程使用hashmap

  Collections.synchronizedMap(Map)创建线程安全的map集合

  Hashtable 锁的全表

ConcurrentHashMap(1.5分段锁、1.8是cas):性能和效率好

ConcurrentHashMap

Hashmap    key value 可以为空,

ConcurrentHashMap  key value 不可以为空(put 有校验,防止出现线程问题)

底层数据结构:

Jdk1.7采用的是分段数组+链表

Jdk1.8采用的是数组+链表/红黑二叉树,与hashMap一致。

加锁的方式

    1.7采用Segment分段锁,底层使用的是ReentrantLock。

1.8采用CAS添加新节点,采用synchronized锁定链表/红黑二叉树的首节点,

相对segment分段锁粒度更细,性能更好。Get时都用Volatile修饰,无需加锁。

HashTable(全表锁)VS ConcurrentHashMap

实现:

ConcurrentHashMap 在 Java 7 采用分段锁(segment),

一个哈希表分成若干个小的哈希表,每个小的哈希表有自己的锁,同一时刻可以被多个线程访问。

在 Java 8 中,采用了 CAS 操作来替代了分段锁,提高了性能。

HashTable(全表锁)get/put所有相关操作都是synchronized的,这相当于给整个哈希表加了一把大锁,

多线程访问的时候,只要有一个线程访问或操作该对象,那其他线程只能阻塞。

性能:

ConcurrentHashMap 采用分段锁(Java 5)和 CAS 操作(Java 8),

在多线程场景下,锁的竞争更加细粒度,可以提供更好的并发性能。

Hashtable 采用 synchronized 关键字,可以确保线程安全,但在高并发情况下,

竞争激烈会导致性能下降。

HashMap VS TreeMap

HashMap :适用于在Map中插入、删除和定位元素。

Treemap:适用于按自然顺序或自定义顺序遍历键(key)。

TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序

TreeMap的value可以是null,(没有自定义比较器)key不能为null,compareTo方法会发生空指针异常

HashMap和HashTable区别

1、两者父类不同

HashMap是继承自AbstractMap类,而Hashtable是继承自Dictionary

它们都同时实现了map、Cloneable(可复制)、Serializable(可序列化)这三个接口。

2、对null的支持不同

HashTable:key和value都不能为null

HashMap:key可以为null,但是这样的key只能有一个,

3、安全性不同

HashMap是线程不同步的。

HashTable是线程同步(全表锁),每个方法上都有synchronized 关键字,

ConcurrentHashMap(concurrent并发包),它是HashTable的替代,

  1. 初始容量大小和每次扩充容量大小不同  

HashMap默认的初始化大小为16,之后每次扩容,容量变为原来的 2的整数次幂

Hashtable 默认的初始大小为11,之后每次扩容,容量变为原来的 2n+1

HasmMap和HashSet的区别

方式

hashMap

HashSet

实现

Map接口

Set接口

存储

存储键值对

存储对象

数据添加

Put方法

Add方法

获取方式

get

forEach()遍历

weakHashMap

它是一个“弱键”,它的Key值和Value都可以是null,

其Map中如果这个Key值指向的对象没被使用,此时触发了GC,该对象就会被回收掉的。

其原理主要是使用的WeakReference和ReferenceQueue实现的,

其key就是weakReference,而ReferenceQueue中保存了被回收的 Key-Value。

原文链接:java中Map有哪些实现类和使用场景_java hashmap、treemap、linkedhashmap、concurrenthashm-CSDN博客

HashMap底层实现原理及扩容机制 - dadapeng - 博客园

HashMap原理 — 扩容机制及存取原理 - 简书

【大厂面试题系列】:JDK7、8 HashMap的get()、put()方法流程_hashmap.pualll-CSDN博客

https://juejin.cn/post/6844903921190699022

TreeMap

存储结构是一个平衡二叉树,具体实现方式为红黑树,默认采用自然排序,

会发现其默认采用key由小到大的顺序输出键值对。适用于按照自然排序和自定义排序规则

主要功能用于排序(自然排序或者比较器排序)

LinkedHashMap 

特点在于有序,体现在先进先出FIFIO上。

依靠双向链表和hash表来实现的。 哈希表保证键唯一,链表保证键有序

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/906674.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

多线程和线程同步基础篇学习笔记(Linux)

大丙老师教学视频&#xff1a;10-线程死锁_哔哩哔哩_bilibili 目录 大丙老师教学视频&#xff1a;10-线程死锁_哔哩哔哩_bilibili 线程概念 为什么要有线程 线程和进程的区别 在处理多任务的时候为什么线程数量不是越多越好? Linux提供的线程API 主要接口 线程创建 pth…

希望十一月对我好一点:C++之多态(2)--多态的原理(部分)

多态的原理 虚函数表指针 下⾯编译为32位程序的运⾏结果是什么&#xff08;&#xff09; A. 编译报错 B.运⾏报错C.8 D.12 class Base{public:virtual void Func1(){cout << "Func1()" << endl;}protected:int _b 1;char _ch x;};int main(){Base b…

Java: 遍历 Map

Java: 遍历 Map package animals;import java.util.HashMap; import java.util.Iterator; import java.util.Map;/*** Description :** author : HMF* Date : Created in 15:33 2024/11/1* version :*/ public class Test002 {public static void main(String[] args){Map<S…

基于vue+neo4j 的中药方剂知识图谱可视化系统

前言 历时一周时间&#xff0c;中药大数据R02系统中药开发完毕&#xff0c;该系统通过scrapy工程获取中药数据&#xff0c;使用python pandas预处理数据生成知识图谱和其他相关数据&#xff0c;利用vuespringbootneo4jmysql 开发系统&#xff0c;具体功能请看本文介绍。 简要…

01.如何用DDD重构老项目

学习资料来源&#xff1a;DDD独家秘籍视频合集 https://space.bilibili.com/24690212/channel/collectiondetail?sid1940048&ctype0 文章目录 动机DDD与重构实践重构? 重写从一开始就采用DDD重构步骤1. 添加领域模块2.分离出有价值的代码3.迁移到领域模块4.重复2,3 动机 …

WPF+MVVM案例实战(十八)- 自定义字体图标按钮的封装与实现(ABD类)

文章目录 1、案例效果1、按钮分类2、ABD类按钮实现描述1.文件创建与代码实现2、样式引用与控件封装3、按钮案例演示1、页面实现与文件创建2、运行效果如下3、总结4、源代码获取1、案例效果 1、按钮分类 在WPF开发中,最常见的就是按钮的使用,这里我们总结以下大概的按钮种类,…

Python 5个数据容器

列表&#xff08;list&#xff09; 列表的定义 定义空列表&#xff1a; 变量名 [] 或 变量名 list() 定义变量&#xff1a; 变量名 [元素1&#xff0c;元素2&#xff0c;元素3&#xff0c;... ] 取出列表元素 列表 [下标索引] 从前向后&#xff0c;从0开始&#xff…

使用语言模型进行文本摘要的五个级别(llm)

视频链接&#xff1a;5 Levels Of LLM Summarizing: Novice to Expert

Qt5.15.x源码编译

介绍&#xff1a; QT5.15以上版本已经不提供现成的集成软件了。所以当我们项目中需要用到5.15以上的版本时&#xff0c;只能自己对源码进行编译来生成一个环境了&#xff08;Qt提供了在线升级&#xff0c;但是在线升级中没有MinGW版本了&#xff09; 背景&#xff1a; 我们想要…

Ubuntu 系统、Docker配置、Docker的常用软件配置(下)

前言 书接上文&#xff0c;现在操作系统已经有了&#xff0c;作为程序的载体Docker也安装配置好了&#xff0c;接下来我们需要让Docker发挥它的法力了。 Docker常用软件的安装 1.Redis 缓存安装 1.1 下载 docker pull redis:7.4.1 #可改为自己需要的版本 1.2 创建本地目录存储…

Redis全系列学习基础篇之位图(bitmap)常用命令的解析

文章目录 描述常用命令及解析常用命令解析 应用场景统计不确定时间周期内用户登录情况思路分析实现 统计某一特定时间内活跃用户(登录一次即算活跃)的数量思路分析与实现 描述 bitmap是redis封装的用于针对位(bit)的操作,其特点是计算效率高&#xff0c;占用空间少,常被用来统计…

面试题:JVM(四)

new对象流程&#xff1f;&#xff08;龙湖地产&#xff09; 对象创建方法&#xff0c;对象的内存分配。&#xff08;360安全&#xff09; 1. 对象实例化 创建对象的方式有几种&#xff1f; 创建对象的步骤 指针碰撞&#xff1a;以指针为分界线&#xff0c;一边是已连续使用的…

手写实现call,apply,和bind方法

手写实现call&#xff0c;apply和bind方法 call&#xff0c;apply和bind方法均是改变this指向的硬绑定方法&#xff0c;要想手写实现此三方法&#xff0c;都要用到一个知识点&#xff0c;即对象调用函数时&#xff0c;this会指向这个对象&#xff08;谁调用this就指向谁&#…

【python ASR】win11-从0到1使用funasr实现本地离线音频转文本

文章目录 前言一、前提条件安装环境Python 安装安装依赖,使用工业预训练模型最后安装 - torch1. 安装前查看显卡支持的最高CUDA的版本&#xff0c;以便下载torch 对应的版本的安装包。torch 中的CUDA版本要低于显卡最高的CUDA版本。2. 前往网站下载[Pytorch](https://pytorch.o…

AI驱动无人驾驶:安全与效率能否兼得?

内容概要 如今&#xff0c;人工智能正以其神奇的魔力驱动着无人驾驶的浪潮&#xff0c;带来了无数令人兴奋的可能性。这一领域的最新动态显示&#xff0c;AI技术在车辆的决策过程和实时数据分析中发挥着重要作用&#xff0c;帮助车辆更聪明地应对复杂的交通环境。通过实时监测…

从头开始学PHP之面向对象

首先介绍下最近情况&#xff0c;因为最近入职了且通勤距离较远&#xff0c;导致精力不够了&#xff0c;而且我发现&#xff0c;人一旦上了班&#xff0c;下班之后就不想再进行任何脑力劳动了&#xff08;对大部分牛马来说&#xff0c;精英除外&#xff09;。 话不多说进入今天的…

Systemd:现代 Linux 系统服务管理的核心

Systemd&#xff1a;现代 Linux 系统服务管理的核心 引言 Systemd 是一种现代的系统和服务管理器&#xff0c;用于在 Linux 系统启动时初始化用户空间&#xff0c;并通过服务管理和资源控制实现系统的自动化管理。自发布以来&#xff0c;Systemd 已逐渐取代传统的 SysVinit 和…

Linux初阶——线程(Part3):POSIX 信号量 CP 模型变体

一、什么是 POSIX 信号量 信号量本质就是一个统计资源数量的计数器。​​​​​​​ 1、PV 操作 pv操作就是一种让信号量变化的操作。其中 P 操作可以让信号量减 1&#xff08;如果信号量大于 0&#xff09;&#xff0c;V 操作可以让信号量加 1. 2、信号量类型——sem_t 3…

《女巫攻击:潜伏在网络背后的隐秘威胁与防御策略》

目录 引言 一、基本概念 二、攻击机制 三、Sybil攻击类型 1、直接通信 2、间接通信 3、伪造身份 4、盗用身份 5、同时攻击 6、非同时攻击 四、攻击影响 五、防御措施 总结 引言 随着区块链技术和去中心化网络的迅速发展&#xff0c;网络安全问题也愈发引起关注。其…

Mybatis-plus入门教程

注意版本 jdk 18 springboot 3.1.0 mybatis 3.0.3 mybatisplus 3.5.5 快速入门 构建模块 导入依赖 <properties><maven.compiler.source>18</maven.compiler.source><maven.compiler.target>18</maven.compiler.target><project.build…