集合知识点整理

发布时间 2023-04-20 07:21:27作者: xfcoding

Java中,数组是保存一组对象的最有效的方式,但是数组的大小是固定的,通常在写代码时,我们不知道对象的确切个数,这个时候,JDK提供的容器类帮我们解决这个问题。Java的容器类分为两类:CollectionMap。所有元素序列对象都实现了Collection接口,键值对对象则实现Map接口。在接口和实现之间有一层Abstract的抽象集合类,它们将集合的一些通用功能做了封装,比如toString方法,通用遍历方法,HashMap的几个视图方法,keySetentrySetvalues等,这些抽象的集合类是为类库的自定义实现者而设计的。

ArrayList和LinkedList的区别

  1. 数据结构不同。ArrayList基于数组,LinkedList基于双向链表。
  2. 数据访问效率不同。随机访问数据时,数组快于链表。因为数组能根据索引直接访问到每个数据;而双向链表,它通过前驱跟后继指针来维持数据与数据之间的逻辑关系,随机访问一个数据,LinkedList使用二分查找判断从头还是尾遍历,直到找到该索引位置。
  3. 数据插入与删除效率不同。插入与删除,LinkedList要快于ArrayList。因为LinkedList插入或删除一个元素只涉及三个节点的指针变化;而ArrayList却要改变插入位置后的所有元素的位置。
  4. 占用内存不同。因为LinkedList的元素不仅有数据域,还有两个指针域,它占用内存更大。

第2、3点是理论而言,我测试过一千万条数据(超过可能造成内存溢出)下两种集合操作耗时对比,几乎没有差别,因此实际使用时小数据量无需考虑两者性能问题。

HashMap

HashMap是基于哈希表的Map接口的实现,以键值对的形式存储。它的特点是:

  1. 存取无序
  2. 键和值都可以为null
  3. 键是唯一的
  4. 非线程安全
  5. Java8的数据结构为:数组+链表+红黑树。HashMap的数据载体是一个数组,数组中存放的是链表,当数组长度和阈值达到一定值时为把链表转成红黑树。这个红黑树的数据类型是TreeNode,它继承自链表Node,即数组中存放是的Node或者TreeNode

什么是哈希冲突?哈希冲突是不同的key根据hash函数计算得到了相同的结果。在HashMap中,根据key的hash值计算数组的索引,如果该数组位置已经有值了,说明产生了哈希冲突,这个时候value会存入链表中,当链表中的值达到一定数量会将链表转换为红黑树。

为什么是红黑树?我只了解到红黑树是一种平衡树,平衡树能提高数据查找效率。为啥平衡树能提高数据查找效率?举个例子,一个非平衡的二叉树,如果还是全是右孩子,那查找一个数据是不是得从根开始遍历;如果是平衡的呢,只要跟父节点做一次比较,就能判断下次是走左子树还是右字树,节约至少一半时间。

HashMap的key,重写了equals方法,是否也要重写hashCode方法?答案是要。HashMap是这么判断key是否重复的:

p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))

只有key的哈希值相等且equals相等同时满足时,才会判断为两个key相同。如果HashMap的key只重写了equals方法,不重写hashCode,由此可能导致原本相同的key可能被HashMap判断为不同,导致put进重复的数据,或者get不出来的现象,这是违背HashMap的设计原则的。

TreeMap

TreeMap是有序的,遍历TreeMap时元素会根据key值排序,默认是升序。它的排序是基于Comparable接口或者指定一个排序接口,因此使用TreeMap时,要么键对象要实现Comparable接口,要么个它传入一个Comparator接口的一个实现,否则使用就会报错。

LinkedHashMap

LinkedHashMap是有序的,它继承HashMap,在哈希桶的基础上增加了两个引用,一个before,一个after,且记录了头结点和尾节点,LinkedHashMap就是以此保证元素的插入顺序的。

HashSet

Set内部是基于Map实现的。

HashSet是基于HashMap实现的,它其实就是一个value为空Object对象的一个HashMap,因此他的特点是元素不可重复。

TreeSet

TreeSet也是基于TreeMap的,TreeMap有序,因此TreeSet也是有序的,并且在构造时需要制定一个排序接口或者TreeSet的元素要实现Comparable接口。