Java常见面试题-02-集合

常见的数据结构

常用的数据结构有:数组,栈,队列,链表,树,散列,堆,图等

  • 数组是最常用的数据结构,数组的特点是长度固定,数组的大小固定后就无法扩容了,数组只能存储一种类型的数据,添加,删除的操作慢,因为要移动其他的元素。
  • 是一种基于先进后出(FILO)的数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个存储的数据被第一个读出来)。
  • 队列是一种基于先进先出(FIFO)的数据结构,是一种只能在一端进行插入,在另一端进行删除操作的特殊线性表,它按照先进先出的原则存储数据,先进入的数据,在读取数据时先被读取出来。
  • 链表是一种物理存储单元上非连续、非顺序的存储结构,其物理结构不能只表示数据元素的逻辑顺序,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列的结节(链表中的每一个元素称为结点)组成,结点可以在运行时动态生成。根据指针的指向,链表能形成不同的结构,例如单链表,双向链表,循环链表等。
  • 是我们计算机中非常重要的一种数据结构,同时使用树这种数据结构,可以描述现实生活中的很多事物,例如家谱、单位的组织架构等等。有二叉树、平衡树、红黑树、B树、B+树。
  • 散列表,也叫哈希表,是根据关键码 (key 和 value) 直接进行访问的数据结构, 通过 key 和 value 来映射到集合中的一个位置,这样就可以很快找到集合中的对应元素。
  • 是计算机学科中一类特殊的数据结构的统称,堆通常可以被看作是一棵完全二叉树数组对象
  • 的定义:图是由一组顶点和一组能够将两个顶点相连的边组成的

集合和数组的区别

  • 数组长度固定,集合长度可变
  • 数组中存储的是同一种数据类型的元素,可以存储基本数据类型,也可以存储引用数据类型;集合存储的都是对象,而且对象的数据类型可以不一致。在开发当中一般当对象较多的时候,使用集合来存储对象。

List 和 Map、Set 的区别

  • List 和 Set 是存储单列数据的集合,Map 是存储键值对这样的双列数据的集合;
  • List 中存储的数据是有顺序的,并且值允许重复;
  • Map 中存储的数据是无序的,它的键是不允许重复的,但是值是允许重复的;
  • Set 中存储的数据是无顺序的,并且不允许重复,但元素在集合中的位置是由元素的 hashcode 决定,即位置是固定的(Set 集合是根据 hashcode 来进行数据存储的,所以位置是固定的,但是这个位置不是用户可以控制的,所以对于用户来说 set 中的元素还是无序的)。

List 和 Map、Set 的实现类

List:

  • ArrayList(底层数据结构是数组,查询快,增删慢。线程不安全,效率高)
  • Vector(底层数据结构是数组,查询快,增删慢。线程安全,效率低)
  • LinkedList(底层数据结构是链表,查询慢,增删快。线程不安全,效率高)

Set:

  • HashSet(底层数据结构是哈希表。通过hashCode()与equals()方法来保证元素唯一)
  • LinkedHashSet(底层数据结构是链表和哈希表。由链表保证元素有序,由哈希表保证元素唯一)
  • TreeSet(底层数据结构是红黑树。通过自然排序和比较器排序来保证元素的排序,通过比较的返回值为0来保证唯一性)

Map:

  • HashMap(基于 hash 表的 Map 接口实现,非线程安全,高效,支持 null 值和 null 键)
  • HashTable(线程安全,低效,不支持 null 值和 null 键)
  • LinkedHashMap(线程不安全,是 HashMap 的一个子类,保存了记录的插入顺序)
  • TreeMap(能够把它保存的记录根据键排序,默认是键值的升序排序,线程不安全)

Hashmap 的底层原理

HashMap 在 JDK1.8 之前的实现方式 数组+链表,但是在JDK1.8 后对HashMap 进行了底层优化,改为了由数组+链表或者数组+红黑树实现,主要的目的是提高查找效率。

  • Jdk8 数组+链表或者数组+红黑树实现,当链表中的元素超过了 8 个以后, 会将链表转换为红黑树,当红黑树节点小于等于 6 时又会退化为链表。
  • 当 new HashMap():底层没有创建数组,首次调用 put()方法示时,底层创建长度为 16 的数组,jdk8 底层的数组是:Node[],而非 Entry[],用数组容量大小乘以加载因子得到一个值,一旦数组中存储的元素个数超过该值就会调用 rehash 方法将数组容量增加到原来的两倍,专业术语叫做扩容,在做扩容的时候会生成一个新的数组,原来的所有数据需要重新计算哈希码值重新分配到新的数组,所以扩容的操作非常消耗性能.
  • 默认的负载因子大小为 0.75,数组大小为 16。也就是说,默认情况下,那么当 HashMap中元素个数超过 160.75=12 的时候,就把数组的大小扩展为 216=32,即扩大一倍。
  • 在我们 Java 中任何对象都有 hashcode,hash 算法就是通过 hashcode 与自己进行向右位移 16 的异或运算。这样做是为了计算出来的 hash 值足够随机,足够分散,还有产生的数组下标足够随机。
  • map.put(k,v)实现原理
    (1)首先将 k,v 封装到 Node 对象当中(节点)。
    (2)先调用 k 的 hashCode()方法得出哈希值,并通过哈希算法转换成数组的下标。
    (3)下标位置上如果没有任何元素,就把 Node 添加到这个位置上。如果说下标对应的位置上有链表。此时,就会拿着 k 和链表上每个节点的 k 进行 equal。如果所有的 equals 方法返回都是 false,那么这个新的节点将被添加到链表的末尾。如其中有一个 equals 返回了true,那么这个节点的 value 将会被覆盖。
  • map.get(k)实现原理
    (1) 先调用 k 的 hashCode()方法得出哈希值,并通过哈希算法转换成数组的下标。
    (2) 在通过数组下标快速定位到某个位置上。如果这个位置上什么都没有,则返回 null。如果这个位置上有单向链表,那么它就会拿着参数 K 和单向链表上的每一个节点的 K 进行 equals,如果所有 equals 方法都返回 false,则 get 方法返回 null。如果其中一个节点的K 和参数K 进行equals 返回true,那么此时该节点的value 就是我们要找的value了,get 方法最终返回这个要找的 value。
  • Hash 冲突
    不同的对象算出来的数组下标是相同的这样就会产生 hash 冲突,当单线链表达到一定长度后效率会非常低。
  • 在链表长度大于 8 的时候,将链表就会变成红黑树,提高查询的效率。

ArrayList和linkedList的区别

  • ArrayList的底层是Array(数组)实现,是基于索引的数据结构,它使用索引在数组中搜索和读取数据是很快的。但是要删除数据却是开销很大,因为这需要重排数组中的所有数据。
  • LinkList是一个双链表,在添加和删除元素时具有比ArrayList更好的性能.但在get与set方面弱于ArrayList.当然,这些对比都是指数据量很大或者操作很频繁。

HashMap和HashTable的区别

  • 两者父类不同(HashMap是继承自AbstractMap类,而Hashtable是继承自Dictionary类。)
  • 对外提供的接口不同(Hashtable比HashMap多提供了elements() 和contains() 两个方法。)
  • 对null的支持不同(Hashtable:key和value都不能为null。HashMap:key可以为null,但是这样的key只能有一个,因为必须保证key的唯一性;可以有多个key值对应的value为null。)
  • 安全性不同(HashMap是线程不安全的,在多线程并发的环境下,可能会产生死锁等问题。Hashtable是线程安全的,它的每个方法上都有synchronized 关键字,因此可直接用于多线程中。)
  • 效率不同(虽然HashMap是线程不安全的,但是它的效率远远高于Hashtable,当需要多线程操作的时候可以使用线程安全的ConcurrentHashMap,ConcurrentHashMap虽然也是线程安全的,但是它的效率比Hashtable要高好多倍。因为ConcurrentHashMap使用了分段锁,并不对整个数据进行锁定。)
  • 初始容量大小和每次扩充容量大小不同
  • 计算hash值的方法不同

Hashmap、hashtable、ConcurrentHashMap 区别

HashMap 和 HashTable 区别:

  • HashMap 是非线程安全的,HashTable 是线程安全的。
  • HashMap 的键和值都允许有 null 值存在,而 HashTable 则不行。
  • 因为线程安全的问题,HashMap 效率比 HashTable 的要高。
  • Hashtable 是同步的,而 HashMap 不是。因此,HashMap 更适合于单线程环境,而 Hashtable 适合于多线程环境。一般现在不建议用 HashTable, ① HashTable 是遗留类,内部实现很多没优化和冗余。②即使在多线程环境下, 现在也有同步的 ConcurrentHashMap 替代,没有必要因为是多线程而用HashTable。

HashTable 和 ConcurrentHashMap 区别:

  • HashTable 使用的是 Synchronized 关键字修饰,ConcurrentHashMap 在JDK1.7 是使用了segment锁分段技术来保证线程安全的。在JDK1.8,ConcurrentHashMap 取消了Segment 分段锁,采用 CAS 和 synchronized 来保证并发安全。数据结构跟 HashMap1.8 的结构类似,数组+链表/红黑二叉树。synchronized 只锁定当前链表或红黑二叉树的首节点,这样只要 hash 不冲突,就不会产生并发,效率又提升 N 倍。

欢迎java热爱者了解文章,作者将会持续更新中,期待各位友友的关注和收藏。。。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿胡爱编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值