java通关整理-集合框架(Collection、Map)

网上收集整理,仅供笔记参考学习

一、集合框架

集合框架:用于存储数据的容器。

集合和数组的

1.数组是固定长度的,集合是可变的
2.数组可存储基本数据类型和引用数据类型,集合只能存储引用数据类型
3.数组的元素类型相同,集合的对象可以是不同数据类型

常用的集合类

java集合框架为CollectionMap 两大类
Collection集合主要有ListSet两大接口

Collection

List
Arraylist: Object数组
Vector: Object数组
LinkedList: 双向循环链表

Set
HashSet(无序,唯一):基于 HashMap 实现的,底层采用 HashMap 来保存元素
LinkedHashSet: LinkedHashSet 继承与 HashSet,并且其内部是通过 LinkedHashMap 来实现的。有点类似于我们之前说的LinkedHashMap 其内部是基于 Hashmap 实现一样,不过还是有一点点区别的。
TreeSet(有序,唯一): 红黑树(自平衡的排序二叉树。)

(Queue)

Map

HashMap: JDK1.8之前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突).JDK1.8以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间
LinkedHashMap:LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。
HashTable: 数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的
TreeMap: 红黑树(自平衡的排序二叉树)

HashMap的初始容量:16(2的指数次幂)
负载因子:0.75

Java集合的快速失败机制 “fail-fast”?

是java集合的一种错误检测机制,当多个线程对集合进行结构上的改变的操作时,有可能会产生 fail-fast 机制。

例如:假设存在两个线程(线程1、线程2),线程1通过Iterator在遍历集合A中的元素,在某个时候线程2修改了集合A的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出 ConcurrentModificationException 异常,从而产生fail-fast机制。

原因:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个modCount变量。集合在被遍历期间如果内容发生变化,就会改变modCount的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。

解决办法:

1.在遍历过程中,所有涉及到改变modCount值得地方全部加上synchronized

2.使用CopyOnWriteArrayList来替换ArrayList

add()方法和Put()方法的差别

add()和put()方法都是集合框架中的添加元素的方法。
add()方法应用于collection集合中
put()方法应用于map集合

二、Collection接口

List接口

ArrayListLinkedList 的区别是什么?

数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。
随机访问效率:ArrayListLinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。
增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 增删操作要影响数组内的其他数据的下标。
内存空间占用:LinkedListArrayList 更占内存,因为 LinkedList 的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素。
线程安全:ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;

综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList。

ArrayListVector 的区别是什么?

这两个类都实现了 List 接口(List 接口继承了 Collection 接口),他们都是有序集合

线程安全:Vector使用了 Synchronized 来实现线程同步,是线程安全的,而 ArrayList 是非线程安全的。
性能:ArrayList在性能方面要优于 Vector
扩容:ArrayListVector 都会根据实际的需要动态的调整容量,只不过在 Vector 每次扩容为原来的2倍,而 ArrayList 只会扩容到原来的1.5倍。
Vector类的所有方法都是同步的。可以由两个线程安全地访问一个Vector对象、但是一个线程访问Vector的话代码要在同步操作上耗费大量的时间。

Arraylist不是同步的,所以在不需要保证线程安全时时建议使用Arraylist

阐述 ArrayList、Vector、LinkedList 的存储性能和特性?

ArrayList 和Vector他们底层的实现都是一样的,都是使用数组方式存储数据
LinkedList 使用双向链表实现存储
Vector 中的方法由于加了 synchronized 修饰,因此 Vector 是线程安全容器,但性能上较ArrayList差。

多线程怎么解决ArrayList 的安全

ArrayList 不是线程安全的,如果遇到多线程场景,可以通过 Collections 的 synchronizedList 方法将其转换成线程安全的容器后再使用。

List<String> synchronizedList = Collections.synchronizedList(list);

Set接口

HashSet 的实现原理

HashSet实际上是一个HashMap实例,HashSet中不允许有重复元素,这是因为HashSet是基于HashMap实现的,HashSet中的元素都存放在HashMap的key上面,而value中的值都是统一的一个固定对象private static final Object PRESENT = new Object();

HashSet如何检查重复

向HashSet 中add ()元素时,判断元素是否存在的依据,不仅要比较hash值,同时还要结合equles 方法比较。
原因:HashSet 中的add ()方法会使用HashMap 的put()方法。( HashMap 比较key是否相等是先比较hashcode() 再比较equals())。

private static final Object PRESENT = new Object();
private transient HashMap<E,Object> map;

public HashSet() {
    map = new HashMap<>();
}
public boolean add(E e) {
    // 调用HashMap的put方法,PRESENT是一个至始至终都相同的虚值
	return map.put(e, PRESENT)==null;
}
HashSet与HashMap的区别

在这里插入图片描述

hashCode()与equals()

1.如果两个对象相等,则hashcode一定也是相同的
2.两个对象相等,对两个equals方法返回true
3.两个对象有相同的hashcode值,它们也不一定是相等的
4.综上,equals方法被重写,则hashCode方法也必须被重写
5.hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)。

==与equals的区别

一个是判断地址是否相同,一个是判断值是否相同
==指引用是否相同,equals()指的是值是否相同

Queue接口

Queue 中 poll()和 remove()有什么区别?
相同点:都是返回第一个元素,并在队列中删除返回的对象。
不同点:如果没有元素 poll()会返回 null,而 remove()会直接抛出 NoSuchElementException 异常。

三、Map接口

hashmap

HashMap底层结构:

java1.7的时候是数组+链表
java1.8之后的时候是数组+链表 --------> 红黑树
当链表中的节点数据超过八个之后,该链表会转为红黑树来提高查询效率
链表下8个节点,加上数组上面的一个节点,实际节点数为9个

hash冲突

在数组的同一个位置的hashcode值相同,发生hash冲突
java1.7链表插入是头插法----->在多线程进行扩容时,会产生死锁
java1.8是尾插法---------------->解决了死锁问题

HashMap怎么解决hash冲突

总结:
HashMap是使用了哪些方法来有效解决哈希冲突的:

  1. 使用链地址法(使用散列表)来链接拥有相同hash值的数据;
  2. 使用2次扰动函数(hash函数)来降低哈希冲突的概率,使得数据分布更平均;
  3. 引入红黑树进一步降低遍历的时间复杂度,使得遍历更快;

JDK1.7和JDK1.8中hashmap不同方式

在Java中,保存数据有两种比较简单的数据结构:数组和链表。数组的特点是:寻址容易,插入和删除困难;链表的特点是:寻址困难,但插入和删除容易;所以我们将数组和链表结合在一起,发挥两者各自的优势,使用一种叫做拉链法/链地址法的方式可以解决哈希冲突。
JDK1.8之前----用拉链法/链地址法(数组+链表),解决hash冲突
JDK1.8之后----加入了红黑树,当链表长度大于阈值(默认为8)时,将链表转化为红黑树

在这里插入图片描述

java1.7位运算取代取模

底层不是用取模运算,而是采用的与长度-1做位运算。作用:效率比取模更高
数据存储较多时,会触发多次扩容!会有多次的rehash,位运算的效率较高

hashMap线程是不安全的
数据会丢失
死锁------产生环状

取余(%)操作中如果除数是2的幂次则等价于与其除数减一的与(&)操作
前提是: length 是2的 n 次方,这是原因之一

如果使用Object作为HashMap的Key,应该怎么办呢?

答:重写hashCode()和equals()方法

HashMap 与 HashTable 区别?

线程不安全、线程安全
效率高、
key可以为null仅为一个,可以有多个null的value值;没有null,报错
初始容量16----2倍; 11------2倍+1
HashTable 基本被淘汰

HashMap和TreeMap区别:

1.实现方式的区别:
HashMap:基于哈希表实现。TreeMap:基于红黑树实现。
2.TreeMap能够把它保存的记录根据键排序.
3.HashMap:适用于在Map中插入、删除和查找元素。
Treemap:适用于按自然顺序或自定义顺序遍历键(key)。
4.HashMap通常比TreeMap快一点。

TreeMap 和 TreeSet 在排序时如何比较元素?

TreeMap 要求存放的键值对映射的键必须实现 Comparable 接口,从而根据键对元素进 行排序。
TreeSet 要求存放的对象所属的类必须实现 Comparable 接口,该接口提供了比较元素的 compareTo()方法,当插入元素时会回调该方法比较元素的大小。

Collections 工具类中的 sort()方法如何比较元素?

Collections 工具类的 sort 方法有两种重载的形式,

第一种要求传入的待排序容器中存放的对象比较实现 Comparable 接口以实现元素的比较;

第二种不强制性的要求容器中的元素必须可比较,但是要求传入第二个参数,参数是Comparator 接口的子类型(需要重写 compare 方法实现元素的比较),相当于一个临时定义的排序规则,其实就是通过接口注入比较元素大小的算法,也是对回调模式的应用(Java 中对函数式编程的支持)。

猜你喜欢

转载自blog.csdn.net/weixin_45773603/article/details/108033084