JavaSE内容整理八(集合方面知识总结,涉及源码分析,扩容机制等,面试重点)

泛型与集合

一、泛型

1.泛型作用

2.泛型类
(1)声明类名后使用(E可以是任何其他字母)
(2)泛型类不支持基本数据类型
在这里插入图片描述

3.泛型方法

(1)在泛型类中使用泛型的方法无需再声明泛型
(2)是否拥有泛型方法,与其所在的类是否泛型没有关系。要定义泛型方法,只需将泛型参数列表置于返回值前。
在这里插入图片描述
4.泛型接口
泛型接口,与泛型类完全相同
Public interface TestGenerics{
Public T getT(T t);
Public String assume(T t);
}
二、集合
1.什么是集合?存储对象的容器
2.集合与数组的区别
(1)数组和集合类都是容器
(2)数组长度是固定的,集合长度是可变的。
(3)数组中可以存储基本数据类型,集合只能存储对象
(4)数组中存储数据类型是单一的,集合中可以存储任意类型的对象。
3.Java集合框架的三大接口
在这里插入图片描述
4.集合框架的三大接口的特点
Collection: 单列集合,三个子接口
(1) List: 有存储顺序, 可重复
ArrayList: 数组实现, 查找快, 增删慢。由于是数组实现, 在增和删的时候会牵扯到数组增容, 以及拷贝元素. 所以慢。数组是可以直接按索引查找, 所以查找时较快。
LinkedList:链表实现, 增删快, 查找慢。由于链表实现, 增加时只要让前一个元素记住自己就可以, 删除时让前一个元素记住后一个元素, 后一个元素记住前一个元素. 这样的增删效率较高但查询时需要一个一个的遍历, 所以效率较低。
Vector: 数组实现,和ArrayList原理相同, 但线程安全, 效率略低和ArrayList实现方式相同, 但考虑了线程安全问题, 所以效率略低。

(2) Set: 无存储顺序, 不可重复,set中没有索引
HashSet—底层是哈希码表,基于HashMap实现的
TreeSet— 底层是二叉树元素不重复,并且实现了排序。

(3)Queue:队列,实现了先进先出
Map: 键值对—Key值不允许重复,如果重复,则覆盖。
HashMap-- key值的hashCode和equals保证元素唯一性
TreeMap–不仅可以保证key不重复,还可以对value数据进行排序。
HashTable–实现同步
5.什么时候该使用什么样的集合
在这里插入图片描述
6.List
特点:有序插入,可重复
(1)ArrayList
特点:1.底层实现:数组
2.查找快,添加和删除慢
3.每一个ArrayList都有一个初始容量(10),存储数据当快溢出时,就会进行扩容操作。ArrayList的默认扩容扩展后数组大小为:(原数组长度*3)/2+1
4.ArrayList是一个非线程安全的列表

构造方法:ArrayList() 构造一个初始容量为十的空列表。
常用方法:
1.boolean add(E e) 将指定的元素追加到此列表的末尾。
2.E get(int index) 返回此列表中指定位置的元素。
3.void clear() 从列表中删除所有元素。
4.boolean contains(Object o) 如果此列表包含指定的元素,则返回 true
5.E remove(int index) 删除该列表中指定位置的元素。
6.int size() 返回此列表中的元素数。
案例:
在这里插入图片描述
ArrayList底层实现原理
在这里插入图片描述在这里插入图片描述

(2)LinkedList
特点:(1)有序,可重复
(2)底层使用双链表存储,所以查找慢,添加和删除快
(3)LinkedList也是非同步的
常用方法:
LinkedList():构造一个空列表。
1.boolean add(E e) 将指定的元素追加到此列表的末尾。
2.E get(int index) 返回此列表中指定位置的元素。
3.void clear() 从列表中删除所有元素。
4.boolean contains(Object o) 如果此列表包含指定的元素,则返回 true
5.E remove(int index) 删除该列表中指定位置的元素。
6.int size() 返回此列表中的元素数。
7. void addFirst(Object o) 在列表的首部添加元素
8.void addLast(Object o) 在列表的末尾添加元素
9.Object getFirst() 返回列表中的第一个元素
10.Object getLast() 返回列表中的最后一个元素
11.Object removeFirst() 删除并返回列表中的第一个元素
12.Object removeLast() 删除并返回列表中的最后一个元素

案例:
在这里插入图片描述
LinkedList底层实现原理:
(1)单链表:只存储值和下一个结点地址,最后一个结点指向null
(2)单向循环链表:在单链表的基础上,最后一个结点不指向null,而指向头结点
(3)双链表:一个结点存储数据、上一个结点的地址、下一个结点的地址。可以从前往后找,也可以从后往前找。最后一个结点指向null ,第一个结点指向的null
(4)双向循环链表:在双链表的基础上,第一个结点的上一个结点指向最后一个结点,最后一个结点的下一个指向头结点
在这里插入图片描述在这里插入图片描述在这里插入图片描述
(3)Vector
特点:1.Vector与ArrayList相似,但是Vector是同步的。所以说Vector是使用数组实现的线程安全的列表。它的操作与ArrayList几乎一样
2.Vector在进行默认规则扩容时,新数组的长度=原始数组长度*2
案例:
在这里插入图片描述

(4)Stack
特点:先进后出
案例:
在这里插入图片描述
7.Set
(1)HashSet
特点:
1.特点:无序不重复 无索引
2.默认不重复的是虚地址,要想内容不重复,就重写hashcode和equals方法。
3.底层是HashMap实现
常用方法:
boolean add(E e) 将指定的元素添加到此集合(如果尚未存在)。
void clear() 从此集合中删除所有元素。
boolean contains(Object o) 如果此集合包含指定的元素,则返回 true 。
boolean remove(Object o) 如果存在,则从该集合中删除指定的元素。
int size() 返回此集合中的元素个数。
内存泄漏原因:属性值发生变化后,hashCode的返回值变化,导致无法定位到删除的元素,删除失败,内存泄露
案例:
在这里插入图片描述
如果要想要内容重复就不添加,就要重写hashcode和equals方法
在这里插入图片描述
在这里插入图片描述

hashset的实现原理:
往HashSet添加元素的时候,HashSet会先调用元素的hashcode方法得到元素的哈希值,然后通过元素的哈希值经过移位等运算,就可以算出该元素的存储位置。
情况1:如果算出元素存储的位置目前没有任何元素存储,那么该元素可以直接存储在该位置上。
情况2:如果算出元素存储的位置目前已经有其它元素存储了,那么会调用该元素的equals方法与该位置的元素再比一次,如果equals返回的是true,那么该元素与该位置上的元素视为重复元素,不允许添加,如果equals方法返回的是false,那么该元素进行添加。

(2)TreeSet
特点:
1.无序不重复,但是排序,所以TreeSet中存放的对象必须实现Comparable接口,重写compareTo方法
2.底层使用红黑树数据结构实现 存储规则:左小右大
3.如果比较元素的时候,compareTo方法返回的是0,那么该元素就被视为重复元素,不允许添加(TreeSet与hashcode和equals方法是没有任何关系的)

常用方法:
除了基本方法外
E first() 返回此集合中当前的第一个(最低)元素。
E last() 返回此集合中当前的最后(最高)元素。
案例:
在这里插入图片描述
必须实现compareble接口,重写方法,否则报错

在这里插入图片描述

实现原理:

在这里插入图片描述
8.Queue
特点:先进先出
案例:
在这里插入图片描述

9.Map
面试常见问题:
1.HashMap数据结构是什么?
2.JDK8对HashMap优化哪块,为何要优化?
3.HashMap和HashTable和ConCurrentHashMap的区别
4.HashMap的扩容机制
5.List的两种实现类有什么区别
6.hashtable和concurrenthashmap底层结构区别,后者有那两个部分,画出数据结构.
7.list、set、map的底层实现原理

(1)HashMap
特点:
1.底层实现1.7之前:数组+链表 1.8以后:数组+链表+红黑树
2.如果key的值相同,后添加的数据会覆盖之前的数据
3.HashMap是非线程安全的

底层实现原理:
1.数据结构:1.7之前:数组+链表 1.8以后:数组+链表+红黑树
2.存储数据:
(1)数组默认长度:
在这里插入图片描述
(2)存储位置 = f(关键字)
在这里插入图片描述
在这里插入图片描述

(3)计算数组下标:n为数组长度  

在这里插入图片描述
(4)为什么这么计算?为了保证不超出范围
假设hash值为87,计算位置
16:…0001 0000
15:…0000 1111
h: …0101 0111
&: 两位同时为1才为1,只有两位都是1的才为1,其它都为0.
15&h:…0000 0111 无论怎么变化取值范围都是0-15,保证下标不会超出范围
16&h: …0001 0000 无论怎么变化都是0-16
综上,所以使用 (数组长度-1)&hash来计算。

3.查找数据:查找操作同理,先通过哈希函数计算出实际存储地址,然后从数组中对应地址取出即可。

4.哈希冲突,也叫哈希碰撞:当我们对某个元素进行哈希运算,得到一个存储地址,然后要进行插入的时候,发现已经被其他元素占用了。

5.如何解决哈希冲突:哈希冲突的解决方案有多种:开放定址法(发生冲突,继续寻找下一块未被占用的存储地址),再散列函数法,链地址法,而HashMap即是采用了链地址法,也就是数组+链表的方式。
测试案例:简化存储位置算法
在这里插入图片描述
测试结论:

在这里插入图片描述

6.红黑树:使用常量 TREEIFY_THRESHOLD 来控制是否切换到平衡树来存储。这个常量值是8,当有超过8个元素的索引一样时,HashMap会使用树来存储它们。目的是为了增加查询效率。因为链表的查询效率比较低,当长度过长的时候就进行优化。

在这里插入图片描述

7.HashMap实现原理图
1.7之前 数组+链表实现,插入在链表的头部,1.8之后(数组+链表+红黑树)是插入在链表的尾部
在这里插入图片描述

8.JDK1.8扩容机制(resize):重新计算数组容量
(1)生成一个初始容量16的数组+链表结构,使用容量大于0.75f时,自动扩容为原来长度的2倍(2的n次幂)。
(2)HashMap不是无限扩容的,当达到了MAXIMUM_CAPACITY,就不再进行扩容。
static final int MAXIMUM_CAPACITY = 1 << 30;
9.为什么扩容的时候要使用2次方数。
(1) 在这里插入图片描述

为了保证,length-1之后低位都是1。
(2)为了减少碰撞几率。
注:hashmap中默认的数组大小是16,是因为16是2的整数次幂的,在小数据量的情况下16比15和20更能减少key之间的碰撞,而加快查询的效率。

9.JDK8中对HashMap做了哪些优化?
(1)扩容后计算元素位置的方式上不太一样,jdk1.7需要与新的数组长度进行重新hash运算,这个方式是相对耗性能的,而jdk1.8新的位置为元素在原数组的位置+原数组的长度。
(2)当链表长度大于8时,转化为红黑树结构。
(3)hash方法不同,1.8更简化
10.为何要优化?
即使负载因子(使用容量)和Hash算法设计的再合理,也免不了会出现拉链过长的情况,一旦出现拉链过长,则会严重影响HashMap的性能。所以在JDK1.8版本中,对数据结构做了进一步的优化,当链表长度太长(默认超过8)时,链表就转换为红黑树,利用红黑树快速增删改查的特点提高HashMap的性能。

常用方法:
1.Object put(Object key, Object val) 以“键-值对”的方式进行存储
2.Object get (Object key) 根据键返回相关联的值,如果不存在指定的键,返回null
3.Object remove (Object key) 删除由指定的键映射的“键-值对”
4.int size() 返回元素个数
5.Set keySet () 返回键的集合
6.Collection values () 返回值的集合
7.boolean containsKey (Object key) 如果存在由指定的键映射的“键-值对”,返回true

案例:在这里插入图片描述

(2)TreeMap
特点:
1.TreeMap基于红黑树数据结构的实现,键值可以使用Comparable或Comparator接口, 重写compareTo方法来排序。

常用方法:同HashMap
(1)K firstKey() 返回此地图中当前的第一个(最低)键。
(2)K lastKey() 返回当前在此地图中的最后(最高)键。

案例:在这里插入图片描述

如果key是对象,需要实现接口,重写方法
在这里插入图片描述在这里插入图片描述在这里插入图片描述

(3)ConcurrentHashMap
特点:
1.ConcurrentHashMap是线程安全并且高效的HashMap。

案例:在这里插入图片描述

底层实现原理:
Hashtable低效主要是因为所有访问Hashtable的线程都争夺一把锁。如果容器有很多把锁,每一把锁控制容器中的一部分数据,那么当多个线程访问容器里的不同部分的数据时,线程之前就不会存在锁的竞争,这样就可以有效的提高并发的访问效率。所以使用ConcurrentHashMap。

数据结构:
(1)JDK8 :ConcurrentHashMap的数据结构(数组+链表+红黑树),桶中的结构可能是链表,也可能是红黑树,红黑树是为了提高查找效率。
采用CAS+Synchronized保证线程安全。
在这里插入图片描述

(2)JDK7:ConcurrentHashMap的数据结构(数组+链表)
 采用分段锁技术保证线程安全,将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问,能够实现真正的并发访问
在这里插入图片描述
从上面的结构我们可以了解到,ConcurrentHashMap定位一个元素的过程需要进行两次Hash操作。第一次Hash定位到Segment,第二次Hash定位到元素所在的链表的头部。
(4)HashTable
特点:Hashtable,它的操作接口和HashMap相同,和HashMap的区别在于:Hashtable是线程安全的,而HashMap是非线程安全的。不推荐使用。

案例:在这里插入图片描述

(5)HashMap和HashTable的区别?HashTable和ConcurrentHashMap的区别?
相同点:HashMap和HashTalbe都可以使用来存储key–value的数据。
区别:
1、继承关系.
在这里插入图片描述在这里插入图片描述

2、HashMap是可以把null作为key或者value的,而HashTable是不可以的。
3、HashMap是线程不安全的,效率较高。而HashTalbe是线程安全的(在hashtable中,方法做了synchronize关键字的同步处理),效率较低。
4、扩容方式不一样:HashMap默认的初始化大小为16,之后每次扩充为原来的2倍。HashTable默认的初始大小为11,之后每次扩充为原来的2n+1。

我想线程安全但是我又想效率高?
JDK1.7:ConcurrentHashMap通过把整个Map分为N个Segment(类似HashTable),可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。

(6)Properites
特点:
1.Properites类是Hashtable类的子类,所以也间接地实现了Map接口
2.在实际应用中,常使用Properties类对属性文件进行处理
常用方法:

  1. load():加载文件;
    2.getProperty(key):通过key值获得对应的value值
    3.setProperty(String key,String value): 给properties文件中写值

使用:
public class TestProperties {

public static void main(String[] args) {
	Properties props = new Properties();
	try {
		props.load(new FileInputStream(new 					File("src/com/chinasofti/test.properties")));
	} catch (FileNotFoundException e) {
		e.printStackTrace();
	} catch (IOException e) {
		e.printStackTrace();
	}
	System.out.println(props.getProperty("name"));
	System.out.println(props.getProperty("password"));
	System.out.println(props.getProperty("age"));
}

}
在这里插入图片描述
10.迭代器遍历
常用方法:
(1)Iterator iterator() 以正确的顺序返回该列表中的元素的迭代器。
(2)boolean hasNext() 如果仍有元素可以迭代,则返回 true
(3)E next() 返回迭代的下一个元素

原理:
在这里插入图片描述

案例:在这里插入图片描述

11.lambda表达式遍历
list.forEach(n->System.out.println())

//使用迭代器遍历
Iterator it = list.iterator(); //返回迭代器对象
while(it.hasNext()){//如果仍有元素可以迭代,则返回 true
System.out.println(it.next()); //返回迭代的下一个元素
}

	//for循环
	for (int i = 0; i < list.size(); i++) {
		System.out.println(list.get(i));
	}
	//增强for循环
	for (String str : list) {
		System.out.println(str);
	}
	
	//lambda表达式遍历
	list.forEach(n->System.out.println(n));

12.Collections工具类
方法:在这里插入图片描述

案例:在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值