笔记-Java集合

Java集合

Collection

List、Set都是单列数据的集合,Map是存储键值(key:value)的双列数据集合

1.集合只能存放对象。比如你存入一个int型数据66放入集合中,其实它是自动转换成Integer类后存入的(自动装箱),Java中每一种基本数据类型都有对应的引用类型。

2.集合存放的都是对象的引用,而非对象本身。所以我们称集合中的对象就是集合中对象的引用。对象本身还是放在堆内存中。

3.集合可以存放不同类型,不限数量的数据类型。

https://www.cnblogs.com/lixiansheng/p/11348050.html

极客学院wiki:

http://wiki.jikexueyuan.com/project/java-collection/

img

List接口

特征:有序,可重复

以下是实现类:

底层数据结构线程安全
ArrayList数组不安全
Vector数组安全
LinkedList循环双向链表不安全
ArrayList
  1. 动态数组,查找效率高

  2. 怎么动态扩容的?add(E e)方法里会先判段实际元素个数+1后的值与当前容量谁大,后者大就进入grow()方法,用 Arrays.copyOf(elementData, newCapacity) 扩容,扩容量是50%,oldCapacity>>1, 除以2的操作

https://blog.csdn.net/weixin_39040059/article/details/78995043

  1. Array(数组)和ArrayList(列表)

Array可以包含基本类型和引用类型,ArrayList只能存放后者

  1. 线程不安全

    多线程下:elementData[size++] = e 设置值的操作同样会导致线程不安全,不是原子操作,多线程下,A线程放入数据,还没执行size++,但是B线程进来了,同样位置放入数据,size++。 https://blog.csdn.net/u012859681/article/details/78206494

    添加 synchronized关键字;

    使用Collections.synchronizedList(); 此方法适合不需要使用Iterator、对性能要求也不高的情况。 https://www.cnblogs.com/hongdada/p/10931891.html

数组大小固定,列表可以动态扩容

列表方法多,addAll()

LinkedList

循环双向链表,增加/删除效率高

LinkedList还实现了Queue接口

Vector

线程安全,扩容量为100%

Set接口

特征:唯一,由hashCode()方法和equals()方法得到唯一的元素。

是否有序:是否按照元素添加的顺序来存储对象?

以下是实现类:

底层数据结构顺序线程安全
HashSet哈希表无序不安全
TreeSet红黑树自然排序
LinkedHashSet链表+哈希表FIFO,自然排序
HashSet

底层是HashMap,无重复元素,没有顺序,线程不安全,只能有一个Null

在存储元素时,调用HashMap的put()方法前首先会调用hashcode()计算出存储位置,然后调用equals()检查是否有相同元素,如果有就无法存储,所有的Key都有一个默认value(Object对象)。

https://blog.csdn.net/qq_32575047/article/details/78901492

TreeSet

是基于TreeMap实现的,无重复,写入的顺序与实际存储(会排序)不相同

TreeSet的排序方法:1.自然排序(自定义对象时重写) 2.比较器排序

LinkedHashSet

根据元素的hashCode值来决定元素的存储位置,但是它同时使用链表维护元素的次序。 也就是插入和实际排序一致。

Queue

Map

顺序效率线程安全允许Null
TreeMap有序
HashMap无序不安全允许
HashTable无序安全不允许

TreeMap:红黑树,是否允许为null要看情况,如下图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SYB9mKWQ-1575097689103)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\1569388283197.png)]

除了Vector和HashTable是线程安全,其他都不是

TreeMap
HashMap

HashMap实际上是一个“链表散列”的数据结构,用链地址法(还有二次探测法,线性探测法)解决碰撞( 根据同一散列函数计算出的散列值相同的现象 ),即数组和链表的结合体。 用来存储Key-Value键值对的集合。

并发性

​ 低,Collections.sychronizedXXX

​ 中,concurrentHashMap

​ 高且排序,concurrentSkipListMap

图1

变量含义
size当前Map里的KV对个数
loadFactor加载因子: 表示Hsah表中元素的填满的程度 ;默认0.75。
threshold临界值=capacity*loadFactor
capacity容量(2的幂),默认初始容量为16。

https://mp.weixin.qq.com/s?__biz=MzI3NzE0NjcwMg==&mid=2650121339&idx=1&sn=7b6bfd4b16b65972271cdb929134496b&chksm=f36bb95ac41c304cace48901fc1be5fd6a13825b6443c331182f636997a05c792a82a9cb27aa&scene=21#wechat_redirect

面试题

https://www.jianshu.com/p/939b8a672070

  1. 如何存储元素?

    当我们往 HashMap 中 put 元素的时候,先根据 key 的 hashCode 重新计算 hash 值,根据 hash 值位运算得到这个元素在数组中的位置(即下标,哈希桶),如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。

    在HashMap里存储自定义对象,如果不重写hashcode()和equals()会直接调用Object类的这俩方法, 比较的是内存地址 。

  2. 为什么不是线程安全?

    在扩容操作里,多线程的环境可能存在其他元素也在put操作,如果hash值相同,可能出现同一位置下用环形链表表示,造成闭环,导致get操作死循环。

    具体就是 线程a 一个节点插入完成后,还没有执行 e = next ,线程 b 执行 next = e.next , 线程a此时执行 e = next,此时就变成刚刚插入节点的下一个节点了,然后调用 e.next = newTable[i] , 这样第二个线程就会变成一个死循环了。

    https://mp.weixin.qq.com/s?__biz=MzI2NjA3NTc4Ng==&mid=2652079766&idx=1&sn=879783e0b0ebf11bf1a5767933d4e61f&chksm=f1748d73c6030465fe6b9b3fa7fc816d4704c91bfe46cb287aefccee459153d3287172d91d23&scene=21#wechat_redirect

  3. 为什么初始容量是16?

    太小的话,容易发送扩容会影响性能,所以在使用的时候,要是预知HashMap中需要存放的个数,预设元素的个数能够有效的提高HashMap的性能 ; 太大浪费空间,也影响效率

  4. HashMap数组大小为什么是2的幂次方?

    &位运算,内存中计算, 该位运算只能用于除数是2的n次方的数的求余

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wQK001dG-1575097689133)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20191104204744968.png)]

    是为了减少散列碰撞和避免空间浪费吧,java的散列函数是通过hashcode和length-1计算的,他的容量必须是2的n次方。如果一开始是14二进制1110,最后一位为0,导致散列函数计算后0001,0101等永远不会出现,位置不能存放元素,空间浪费,增加了散列碰撞。散列函数的设计就是围绕减少散列碰撞和使元素分布均匀(整个数组都能存放)而设计的。

    在计算数组下标时,是通过hash & (length-1)得到的,把length设计成2^n,相当于取模运算,同时最后一位为1,则位运算的结果最后一位可能为1或0, 保证了散列的均匀,同时也提升了效率

    https://www.nowcoder.com/discuss/29000

    https://blog.csdn.net/sidihuo/article/details/78489820

  5. 如何扩容?为什么要扩容?

    why?如果我们没有设置初始容量大小,随着元素的不断增加,HashMap会发生多次扩容,而HashMap中的扩容机制决定了每次扩容都需要重建hash表,是非常影响性能的。

  6. 1.8的改进?

    JDK1.8中加入了红黑树是为了防止哈希表碰撞攻击,当链表链长度为8时,及时转成红黑树,提高map的效率。

    扩容改进https://www.cnblogs.com/Xieyang-blog/p/8886921.html

  7. 为什么是链表大小超过8才转换成树?

    根据泊松分布计算出桶中元素的个数和概率的对照表, 链表中元素个数为8时的概率已经非常小

HashTable

Key/Value都不能为null

concurrentHaspMap

面试题

  1. 如何为集合排序?

    如果是基础数据类型和String,直接调用CollectionUtils.sort(x)方法;自定义对象则需要实现Compareable接口,然后重写compareTo()方法

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值