Java集合面试题(持续更新)

本文详细介绍了Java集合面试中常见的问题,包括ArrayList、LinkedList、HashSet、TreeSet、HashMap和LinkedHashMap等的特性和使用细节,如扩容机制、增删查改性能、线程安全等,帮助读者深入理解Java集合的内部实现和选择合适的数据结构。
摘要由CSDN通过智能技术生成

集合和数组的区别

使用集合的优点

1、数组能存放基本数据类型和对象,而集合类存放的都是对象,集合类不能存放基本数据类型(你存储的是简单的int,它会自动装箱成Integer)。数组和集合存放的对象皆为对象的引用地址
数组容易固定无法动态改变,集合类容量动态改变。
2、数组无法判断其中实际存有多少元素,length只告诉了数组的容量,而集合的size()可以确切知道元素的个数
3、集合有多种实现方式和不同适用场合,不像数组仅采用顺序表方式
4、集合以类的形式存在,具有封装、继承、多态等类的特性,通过简单的方法和属性即可实现各种复杂操作,大大提高了软件的开发效率
 

集合体系概述

在这里插入图片描述

Collection集合

数据结构:只是定义了他们的存储方式,具体的值List以对象为值存,Map以KV为值存。

List集合的特点就是:有序(存储顺序和取出顺序一致),可重复
• ArrayList:数组实现,查询快,增删慢,轻量级;(线程不安全)
• LinkedList:双向链表实现,增删快,查询慢 (线程不安全)经常用在增删操作较多而查询操作很少的情况下:
• Vector:数组实现,重量级 (线程安全、使用少)

现在要想在多线程的环境下,使用一个线程安全的集合怎么实现?

1、CopyOnWriteArrayList

特性:CopyOnWriteArrayList是线程安全的List实现,通过对底层数组进行复制来实现线程安全。读操作不会阻塞,而写操作会创建一个新的数组进行修改,确保写操作不影响读操作。
用法:CopyOnWriteArrayList适用于读操作频繁、写操作较少的场景,例如缓存或只读数据。它提供了线程安全的遍历,但写操作的开销较高。

public static void testSynchronizedList(){
        List<Integer> list = Collections.synchronizedList(new ArrayList<Integer>());
        long time1 = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            list.add(i);
        }
        long time2 = System.currentTimeMillis();
        System.out.println("synchronizedList: "+(time2-time1));
    }

    public static void testCopyOnWriteArrayList(){
        CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();
        long time1 = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            list.add(i);
        }
        long time2 = System.currentTimeMillis();
        System.out.println("copyOnWriteArrayList: "+(time2-time1));
    }


Set集合的特点是:元素不可重复
• HashSet:底层是操作HashMap,数据结构是哈希表 (数组+链表 / 红黑树)
• TreeSet(Set的子接口SortedSet接口的实现类): 底层是操作TreeMap,数据结构是红黑树(是一个自平衡的二叉树),保证元素的排序方式
• LinkedHashSet:底层数据结构由哈希表 (数组+链表 / 红黑树),并加了一个链表组成

Map集合

• HashMap:键值对,key不能重复,但是value可以重复;key的实现就是HashSet;value对应着放;允许null的键或值;
• Hashtable:线程安全的,不允许null的键或值; Properties::key和value都是String类型,用来读配置文件;
• TreeMap:对key排好序的Map; key 就是TreeSet, value对应每个key;key要实现Comparable接口或TreeMap有自己的构造器;
• LinkedHashMap:此实现与HashMap的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。存储的数据是有序的。

Collection——>List——>ArrayList

1、ArrayList的add方法:

1)先检查是否需要扩容
2)得到最小容量
3)足够:直接添加,不足够:扩容
4)如果最小容量比数组长度要大,就调用grow()扩容
5)grow()中调用Arrays.copyOf()【向新数组重新拷贝,并扩容为1.5倍】
6)第一次扩容后,如果容量还是小于最小容量(minCapacity),就将容量扩充为最小容量

2、ArrayList必知细节:

• ArrayList是基于动态数组实现的,在增删时候,需要数组的拷贝复制。
• ArrayList的默认初始化容量是10,每次扩容时候增加原先容量的一半,也就是变为原来的1.5倍
删除元素时不会减少容量,若希望减少容量则调用trimToSize()
它不是线程安全的。它能存放null值

3、ArrayList扩容机制?

1)调用ArrayList.ensureCapacity()下面的calculateCapacity(elementData, minCapacity)方法:
2)首先得到当前元素数据(elementData)和属性的长度(oldCapacity)。
3)然后通过判断属性长度(oldCapacity)和最小长度(minCapacity)参数谁大来决定是否需要扩容, 如果最小长度(minCapacity)大于属性长度(oldCapacity),那么我们就对当前的List对象进行扩容。 扩容为原来的1.5倍。然后使用copyOf()数组拷贝的方法,把以前存放的数据转移到新的数组对象中
4)如果最小长度(minCapacity)不大于属性长度(oldCapacity)那么就不进行扩容。
从此方法中我们可以清晰的看出其实ArrayList扩容的本质就是计算出新的扩容数组的size后实例化, 并将原有数组内容复制到新数组中去。

4、 Arraylist 与 LinkedList 区别?

1)底层数据结构不同:
ArrayList底层是Object动态数组,所以ArrayList具有数组的查询速度快的优点以及增删速度慢的缺点(插入和删除元素会移动后面全部的元素向前或者向后一位)。
而在LinkedList的底层是一种双向链表(JDK1.6 之前为循环链表,JDK1.7取消了循环)。在此链表上每一个数据节点都由三部分组成:前指针(指向前面的节点的位置),数据,后指针(指向后面的节点的位置)。
2)因为底层数据结构不同,所以内容空间占用也是不同的:
ArrayList 的空间浪费主要体现在在 list 列表的结尾会预留一定的容量空间,而 LinkedList 的空间花费则体现在它的每一个元素都需要消耗比 ArrayList 更多的空间(因为要存放直接后继和直接前驱以及数据)。
3)因为底层数据结构不同,插入和删除是否受元素位置的影响也是不同的:
ArrayList插入和删除元素会移动后面全部的元素向前或者向后一位,增加则是列表的末尾。LinkedList 采用链表存储,所以,如果是在头尾插入或者删除元素不受元素位置的影响,如果是要在指定位置 i 插入和删除元素的话需要先移动到指定位置再插入。

相同点:ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;

5、Arraylist 与 Vector区别?

1)Vector底层也是数组,与ArrayList最大的区别就是:同步(线程安全)
2)Vector是同步的,在要求非同步的情况下,我们一般都是使用ArrayList来替代Vector的了

【如果想要ArrayList实现同步,可以使用Collections的方法:
List list = Collections.synchronizedList(new ArrayList(…));就可以实现同步了】
3) ArrayList在底层数组不够用时在原来的基础上扩展0.5倍成为原来的1.5倍,Vector是扩展1倍成为原来的2倍。

Collection——>List——>LinkedList

1、LinkedList必知细节:

1)LinkedList底层是双向链表
2)LinkedList还实现了Deque接口,因此,除了链表我们还可以操作LinkedList像操作队列和栈一样。
3)LinkedList变量就这么几个,因为我们操作单向链表的时候也发现了:有了头结点,其他的数据我们都可以获取得到了。(双向链表也同理)
 

Collection——>List小结

ArrayList:
• 底层实现是数组
• ArrayList的默认初始化容量是10,每次扩容时候增加原先容量的一半,也就是变为原来的1.5倍
• 在增删时候,需要数组的拷贝复制(navite 方法由C/C++实现)
LinkedList:
• 底层实现是双向链表[双向链表方便实现往前遍历]
Vector:
• 底层是数组,现在已少用,被ArrayList替代,原因有两个:
– Vector所有方法都是同步,有性能损失。
– Vector初始length是10 超过length时 以100%比率增长,相比于ArrayList更多消耗内存。
总的来说:查询多用ArrayList,增删多用LinkedList。
ArrayList增删慢不是绝对的(在数量大的情况下,已测试):
• 如果增加元素一直是使用add()(增加到末尾)的话,那是ArrayList要快
• 一直删除末尾的元素也是ArrayList要快【不用复制移动位置】
• 至于如果删除的是中间的位置的话,还是ArrayList要快!
但一般来说:增删多还是用LinkedList,因为上面的情况是极端的~

Collection——>Set——>HashSet

底层数据结构是HashMap,即哈希表(数组+链表/红黑树),值是一个一个对象。而不是HashMap的KV值

1、HashSet必知细节

• 实现Set接口
• 不保证迭代顺序
• 允许元素为null
• 底层实际上是一个HashMap实例
• 非同步
• 初始容量非常影响迭代性能
• HashSet是线程非安全的

所以可以直接总结出:HashSet实际上就是封装了HashMap,操作HashSet元素实际上就是操作HashMap。这也是面向对象的一种体现,重用性贼高!

2、HashSet的equals和HashCode(HashSet如何判断元素重复

前面说过,Set集合是不允许重复元素的,否则将会引发各种奇怪的问题。

HashSet需要同时通过equals和HashCode来判断两个元素是否相等,具体规则是,先判断HashCode是否相等,再判断equals( )是否相等。相等则这两个元素相等(即重复)。


试想如果重写了equals方法但不重写hashCode方法,即相同equals结果的两个对象将会被HashSet当作两个元素保存起来(第一步就是调用hashCode方法,结果不同,所以当成了两个元素),这与我们设计HashSet的初衷不符(元素不重复,但他俩实际上是重复的相同元素)。

equals判断相等的元素,hashCode必定相等,所以重写equals,hashCode也一定要重写。重写前后hashCode返回的结果相等(即保证保存在同一个位置)


另外如果两个元素HashCode相等但equals结果不为true,HashSet会将这两个元素保存在同一个位置,并将超过一个的元素以链表方式保存,这将影响HashSet的效率。
如果重写了equals方法但没有重写hashCode方法,则HashSet可能无法正常工作

3、HashSet如何达到不存在重复元素的目的(上一个的问题总结)

“键”就是我们要存入的对象,“值”则是一个常量。这样可以确保,我们所需要的存储的信息是“键”。而“键”在Map中是不能重复的,这就保证了我们存入Set中的所有的元素都不重复。
HashSet如何过滤重复元素()?
调用元素HashCode获得哈希码–》判断哈希码是否相等,不相等则录入 —》相等则判断equals()后是否相等,不相等在进行 hashcode录入,相等不录入

4、HashMap和HashSet区别

 

Collection——>Set——>TreeSet

存储和读数据方式:

1、TreeSet必知细节

• 实现NavigableSet接口
• 可以实现排序功能
• 底层实际上是一个TreeMap实例
• 非同步

2、TreeSet为什么可以排序

TreeSet实现了SortedSet接口,顾名思义这是一种排序的Set集合,查看jdk源码发现底层是用TreeMap实现的,本质上是一个红黑树原理。 正因为它是排序了的,所以相对HashSet来说,TreeSet提供了一些额外的按排序位置访问元素的方法,例如first(), last(), lower(), higher(), subSet(), headSet(), tailSet().
TreeSet的排序分两种类型,一种是自然排序,另一种是定制排序。


3、自然排序(在元素中写排序规则)

TreeSet 会调用compareTo方法比较元素大小,然后按升序排序。所以自然排序中的元素对象,都必须实现了Comparable接口,否则会抛出异常。对于TreeSet判断元素是否重复的标准,也是调用元素从Comparable接口继承而来额compareTo方法,如果返回0则是重复元素(两个元素相等)。Java的常见类都已经实现了Comparable接口。
因为TreeSet会调用元素的compareTo方法,这就要求所有元素的类型都相同,否则也会发生异常。也就是说,TreeSet只允许存入同一类的元素。

4、定制排序(在集合中写排序规则)

TreeSet还有一种排序就是定制排序,定制排序时候,需要关联一个Comparator对象,由Comparator提供排序逻辑。

5、其他知识点

TreeSet是依靠TreeMap来实现的。
TreeSet是一个有序集合,TreeSet中元素将按照升序排列,缺省是按照自然顺序进行排列,意味着TreeSet中元素要实现Comparable接口。
我们可以在构造TreeSet对象时,传递实现了Comparator接口的比较器对象。

6、Comparable和Comparator

Comparable 接口提供自然排序顺序。
对于那些没有自然顺序的类、或者当您想要一个不同于自然顺序的顺序时,您可以实现Comparator 接口来定义您自己的排序函数。可以将Comparator传递给Collections.sort或Arrays.sort。

Comparator接口
当一个类并未实现Comparable,或者不喜欢缺省的Comaparable行为。可以实现Comparator接口
直接实现Comparator的compare接口完成自定义比较类。
例:Arrays.sort(results, new Comparator() 数组排序 RepDataQueryExecutor
例:Collections.sort(lst,new Comparator()

Collection——>Set——>LinkedHashSet

1、LinkedHashSet必知细节

• 迭代是有序的
• 允许为null
• 底层实际上是一个HashMap+双向链表实例(其实就是LinkedHashMap)…
• 非同步
• 性能比HashSet差一丢丢,因为要维护一个双向链表
• 初始容量与迭代无关,LinkedHashSet迭代的是双向链表

LinkedHashSet是HashSet的一个子类,LinkedHashSet也根据HashCode的值来决定元素的存储位置,但同时它还用一个链表来维护元素的插入顺序,插入的时候即要计算hashCode又要维护链表,而遍历的时候只需要按链表来访问元素。
LinkedHashSet本质上也是从LinkedHashMap而来,LinkedHashSet的所有方法都继承自HashSet, 而它能维持元素的插入顺序的性质则继承自LinkedHashMap

Collection——>Set小结

可以很明显地看到,Set集合的底层就是Map。
下面总结一下Set集合常用的三个子类吧:set集合的特点是元素不可重复
1)HashSet:无序,允许为null,底层是HashMap,即哈希表 (数组+链表 / 红黑树),非线程同步
2)TreeSet:有序,不允许为null,底层是TreeMap(红黑树(是一个自平衡的二叉树)),非线程同步
3)LinkedHashSet:迭代有序,允许为null,底层是HashMap+双向链表(哈希表 (数组+链表 / 红黑树),并加了一个链表组成),非线程同步
从结论而言我们就可以根据自己的实际情况来使用了。

几种Set的比较

1)HashSet外部无序地遍历成员。
成员可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法。
2)TreeSet外部有序地遍历成员;
附加实现了SortedSet, 支持子集等要求顺序的操作
成员要求实现Comparable接口,或者使用Comparator构造TreeSet。成员一般为同一类型。
3)LinkedHashSet外部按成员的插入顺序遍历,成员与HashSet成员类似
HashSet是基于Hash算法实现的,其性能通常都优于TreeSet。我们通常都应该使用HashSet,在我们需要排序的功能时,我们才使用TreeSet。

HashSet的元素存放顺序和我们添加进去时候的顺序没有任何关系,而LinkedHashSet 则保持元素的添加顺序。TreeSet则是对我们的Set中的元素进行排序存放。

一般来说,当您要从集合中以有序的方式抽取元素时,TreeSet实现就会有用处。为了能顺利进行,添加到 TreeSet 的元素必须是可排序的。 而您同样需要对添加到TreeSet中的类对象实现 Comparable 接口的支持。一般说来,先把元素添加到 HashSet,再把集合转换为 TreeSet 来进行有序遍历会更快。

各种Set集合性能分析

HashSet和TreeSet是Set集合中用得最多的I集合。HashSet总是比TreeSet集合性能好,因为HashSet不需要额维护元素的顺序。
LinkedHashSet需要用额外的链表维护元素的插入顺序,因此在插入时性能比HashSet低,但在迭代访问(遍历)时性能更高。因为插入的时候即要计算hashCode又要维护链表,而遍历的时候只需要按链表来访问元素。
EnumSet元素是所有Set元素中性能最好的,但是它只能保存Enum类型的元素


Map集合与Collection的区别

  1. Map集合存储元素是成对出现的,Map的键是唯一的,值是可以复制的
  2. Collection集合存储元素是单独出现的,Collection的儿子Set是唯一的,List是可重复的

散列表

1、介绍

无论是Set还是Map,我们会发现都会有对应的–>HashSet,HashMap
首先我们也先得回顾一下数组和链表:
• 链表和数组都可以按照人们的意愿来排列元素的次序,他们可以说是有序的(存储的顺序和取出的顺序是一致的)
• 但同时,这会带来缺点:想要获取某个元素,就要访问所有的元素,直到找到为止。
• 这会让我们消耗很多的时间在里边,遍历访问元素~
而还有另外的一些存储结构:不在意元素的顺序,能够快速的查找元素的数据,其中就有一种非常常见的:散列表

2、工作原理

散列表为每个对象计算出一个整数,称为散列码。根据这些计算出来的整数(散列码)保存在对应的位置上!在Java中,散列表用的是链表数组实现的,每个列表称之为桶。
一个桶上可能会遇到被占用的情况(hashCode散列码相同,就存储在同一个位置上),这种情况是无法避免的,这种现象称之为:散列冲突

  1. 此时需要用该对象与桶上的对象进行比较,看看该对象是否存在桶子上了~如果存在,就不添加了,如果不存在则添加到桶子上
  2. 当然了,如果hashcode函数设计得足够好,桶的数目也足够,这种比较是很少的~
  3. 在JDK1.8中,桶满时会从链表变成平衡二叉树(红黑树,是弱平衡二叉树)
  4. 如果散列表太满,是需要对散列表再散列,创建一个桶数更多的散列表,并将原有的元素插入到新表中,丢弃原来的表~
  5. 装填因子(load factor)决定了何时对散列表再散列~
  6. 装填因子默认为0.75,如果表中超过了75%的位置已经填入了元素,那么这个表就会用双倍的桶数自动进行再散列

Collection——>Map——>HashMap

1、HashMap必知细节

Hash的底层是散列表,而在Java中散列表的实现是通过数组+链表的:数组+链表–>散列表
我们可以简单总结出HashMap:
1)键不允许重复,值允许重复
2)HashMap 是一个最常用的Map,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度。
3)无序
4)HashMap最多只允许一条记录的键为Null;允许多条记录的值为 Null;
5)HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap;可能会导致数据的不一致。如果需要同步,可以用 Collections的synchronizedMap方法使HashMap具有同步的能力。(非同步)
6)底层由散列表(哈希表)实现
7)初始容量和装载因子对HashMap影响挺大的,设置小了不好,设置大了也不好

2、HashMap核心方法——put

1)计算了哈希值
2)并对哈希值执行了异或运算

直接将key作为哈希值不就好了吗,做异或运算是干嘛用的?

我们是根据key的哈希值来保存在散列表中的,我们表默认的初始容量是16,要放到散列表中,就是0-15的位置上。也就是tab[i = (n - 1) & hash]。可以发现的是:在做&运算的时候,仅仅是后4位有效~那如果我们key的哈希值高位变化很大,低位变化很小。直接拿过去做&运算,这就会导致计算出来的Hash值相同的很多。

而设计者将key的哈希值的高位也做了运算(与高16位做异或运算,使得在做&运算时,此时的低位实际上是高位与低位的结合),这就增加了随机性,减少了碰撞冲突的可能性!

3、HashMap的实现原理

散列:

Hash哈希算法的意义在于提供了一种快速存取数据的方法,它用一种算法建立键值与真实值之间的对应关系。散列表又称为哈希表。散列表算法的基本思想是:以结点的关键字为自变量,通过一定的函数关系(散列函数)计算出对应的函数值,以这个值作为该结点存储在散列表中地址。
当散列表中的元素存放太满,就必须进行再散列,将产生一个新的散列表,所有元素存放到新的散列表中,原先的散列表将被删除。在Java语言中,通过负载因子(load factor)来决定何时对散列表进行再散列。例如:如果负载因子0.75,当散列表中已经有75%位置已经放满,那么将进行再散列。
负载因子越高(越接近1.0),内存的使用效率越高,元素的寻找时间越长。负载因子越低(越接近0.0),元素的寻找时间越短,内存浪费越多。

何时需重写equals?
当一个类有自己特有的“逻辑相等”概念(不同于对象身份的概念);
Object类仅仅提供了一个对引用的比较,如果两个引用不是同一个那就返回false,这是无法满足大多数对象比较的需要的,所以要覆盖;

使用 == 操作符检查实参是否为指向对象的引用” 用instanceof操作符检查实参是否为正确的类型,把实参转换到正确的类型;对于该类中每一个“关键”域,检查实参中的域与当前对象中对应的域值是否匹配。

对于既不是float也不是double类型的基本类型的域,可以使用 == 操作符进行比较;对于对象引用类型的域,可以递归地调用所引用的对象的equals方法,对于float和double类型的域,先转换成int或long类型的值,然后使用==操作符比较;

当你编写完成了equals方法之后,应该问自己三个问题:它是否是对称的、传递的、一致的? 如果答案是否定的,那么请找到 这些特性未能满足的原因,再修改equals方法的代码

equals()和hashCode()同时覆写,尤其强调当一个对象被当作键值(或索引)来使用的时候要重写这两个方法;
覆写equals后,两个不同实例可能在逻辑上相等,但是根据Object.hashCode方法却产生不同的散列码,违反“相等的对象必须具有相等的散列码”。
导致,当你用其中的一个作为键保存到hashMap、hasoTable或hashSet中,再以“相等的”找另 一个作为键值去查找他们的时候,则根本找不到

不同类型的hashCode取值
如果该域是布尔型的,计算(f?0:1)
如果是char,short,byte或int,计算(int)f
如果是long类型,计算(int)(f^(f>>>32))
如果是float类型,计算Float.floatToIntBits(f)
如果是double类型,计算Dobule.doubleToLongBits(f)
如果该域是一个对象引用,递归调用hashCode
如果该域是一个数组,则把每个元素当做单独的域来处理,对每个重要的元素计算一个散列码

4、HashMap和HashTable的区别

从存储结构和实现来讲基本上都是相同的。它和HashMap的最大的不同是它是线程安全的,另外它不允许key和value为null。Hashtable是个过时的集合类,不建议在新代码中使用,不需要线程安全的场合可以用HashMap替换,需要线程安全的场合可以用ConcurrentHashMap替换

不同点HashMapHashTable
数据结构数组+链表+红黑树数组+链表
基础的类基础AbstractMap基础Dictionary
是否线程安全
性能高低
默认初始化容量16

11

扩容的方式原始容量×2原始容量×2+1
遍历方式Iterator(迭代器)Iterator(迭代器)和Enumeration()枚举器
Iterator(迭代器)遍历数组顺序索引从小到大索引从大到小

5、hashMap总结(重点)

在JDK8中HashMap的底层是:数组+链表(散列表)+红黑树

在散列表中有装载因子这么一个属性,当装载因子*初始容量小于散列表元素时,该散列表会再散列,扩容2倍!

装载因子的默认值是0.75,无论是初始大了还是初始小了对我们HashMap的性能都不好

• 装载因子初始值大了,可以减少散列表再散列(扩容的次数),但同时会导致散列冲突的可能性变大(散列冲突也是耗性能的一个操作,要得操作链表(红黑树)!
• 装载因子初始值小了,可以减小散列冲突的可能性,但同时扩容的次数可能就会变多!
初始容量的默认值是16,它也一样,无论初始大了还是小了,对我们的HashMap都是有影响的:
• 初始容量过大,那么遍历时我们的速度就会受影响~
• 初始容量过小,散列表再散列(扩容的次数)可能就变得多,扩容也是一件非常耗费性能的一件事~

从源码上我们可以发现:HashMap并不是直接拿key的哈希值来用的,它会将key的哈希值的高16位进行异或操作,使得我们将元素放入哈希表的时候增加了一定的随机性。

还要值得注意的是:并不是桶子上有8位元素的时候它就能变成红黑树,它得同时满足我们的散列表容量大于64才行的~
 

Collection——>Map——>LinkedHashMap

1、LindkedHashMap必备知识点

LinkedHashMap: 此实现与HashMap的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。存储的数据是有序的。

调用的是HashMap构造方法
LinkedHashMap没有put方法,原来LinkedHashMap和HashMap的put方法是一样的!LinkedHashMap继承着HashMap,LinkedHashMap没有重写HashMap的put方法
所以,LinkedHashMap的put方法和HashMap是一样的。
当然了,在创建节点的时候,调用的是LinkedHashMap重写的方法

根据源码注释,我们可以分析出:

• 底层是散列表和双向链表
• 允许为null,不同步
• 插入的顺序是有序的(底层链表致使有序)
• 装载因子和初始容量对LinkedHashMap影响是很大的~
• 初始容量对遍历没有影响
 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值