双列集合简介
双列集合 : 由两根单列集合组成的集合叫双列集合
双列集合的别称 : 键值对集合,映射集合,夫妻对集合
左边的集合: 键集
右边的集合: 值集
映射关系 : 键隐射到值 (username -- password)
夫妻对集合 : 一妻多夫
丈夫 : 键 -> 唯一
妻子 : 值 -> 重复
"奶量" -- "露露"
"Pone" -- "露露"
双列集合具备:
1. 长度可变
2. 只能存对象(基本数据类型元素存储解决方案: 包装类)
3. 键集集合中的元素 唯一
4. 值集集合中的元素 可以重复
5. 元素存取无序 -> 键集是无序的 - "夫唱妇随"

双列集合的体系结构

Map<K,V>
Map<K,V> : 接口 -> 根节点
创建容器:
Map<K,V> 集合 = new 具体子类对象<K,V>();
格式解释:
<K,V> : 因为是双列集合,键和值的元素类型都需要指定
具体使用的时候,拿2个引用数据类型的类名去替换K,V即可
K : 键集元素的数据类型 V : 值集元素的元素类型
增删改查:
增 /改 : V put(K key, V value) : 往集合中添加一对隐射关系
-> 通过分析源码咱们得知 : Map集合中运行有null键,但只能有一个null键
删 :
V remove(Object key) : 根据传入的键删除集合中的一对隐射关系,并把删除的值返回
boolean remove(K key,V value): 根据传入的键值对删除集合中的这对映射关系,返回删除是否成功
void clear() : 清空集合中所有的键值对关系
查 :
V get(Object key) : 根据传入的键返回其对应的值 -> 丈夫找妻子
int size() : 返回集合中有几对元素
Set<K> keySet() : 获取双列集合中所有的键元素,并存入到Set集合 -> 获取键集 -> 召集所有的丈夫
Collection<V> values() : 获取双列集合中所有的键元素,并存入到Collection(元素可重复的)集合 -> 获取值集 -> 召集所有的妻子
boolean isEmpty(): 查询集合是否为空
boolean containsKey(Object key) : 判断集合是否包含传入的键
boolean containsValue(Object value) : 判断集合是否包含传入的值
遍历:
1. 面向过程的方式 (先召集所有的丈夫,再丈夫找媳妇)
a. Set<K> keySet()
b. 遍历存有丈夫的集合
c. 在遍历中 获取丈夫对于的妻子V get(Object key)
2. 面向对象的方式 (召集所有的结婚证对象,再通过结婚证对象获取结婚证对象上的丈夫和妻子)
a. 召集所有的结婚证对象 : Set<Map.Entry<K,V>> entrySet()
Map.Entry<K,V>
Map.Entry : Entry接口是Map接口中的内部接口
Entry<K,V> : Entry类型中封装着键和值
Map.Entry<K,V> : 是存储Entry对象Set集合的泛型
b. 遍历存储结婚证对象的Set集合,取出每一本Entry对象
c. 通过每一个 Entry对象 获取 其封装的键和值
Entry接口中:
K getKey()
V getValue()
HashMap<K,V>
底层为Hash表的Map接口实现:
创建对象
增删改查
遍历
HashMap<K,V>的底层原理
Java中Hash算法去重原理 :
比较2个对象的hash值 && (比较2个对象的地址值 || 新元素.equals(老元素))
this.hashCode() == o.hashCode() && (this == o this.equals(o))
当存放第1024个元素时,HashMap集合底层数组的长度是多少 (不考虑特殊情况)? 2048
1. 当创建一个HashMap集合,源码会在底层创建一个容量为16的数组, 这个数组的加载因子是 0.75
加载因子 : 底层数组扩容的时机
2. 当添加的元素达到加载因子的条件时,底层数组会进行扩容,扩容2倍;
3. 当集合底层数组容量翻倍后,原来在集合中的元素和要添加的元素重新计算应该存放的索引位置
-------------------------------------------------------------------
4. 当添加的元素在底层hash表所对应的索引位置值是 null 的时候, 就算达到了加载因子的条件也不扩容
5.JDK8的时候,哈希表底层实现由数组+链表升级为数组+链表+红黑树!
当链表中元素挂载的数量>=8的时候,会把链表升级为红黑树(升级为红黑树是为了提高查找效率)
特殊情况 : 当底层数组的总元素和 <= 64 的时候, 就算某个链表的挂载元素>=8 也不变红黑树!
LinkedHashMap<K,V>/LinkedHashSet
HashMap<K,V> 存取无序的集合
HashSet<E> 存取无序的集合
但是如果底层数据结构增加了链表后,存取无序的集合就变成存取有序的集合!
链表中的元素Node对象 获取上一个Node对象地址和获取下一个Node对象地址
LinkedHashMap<K,V>/LinkedHashSet<E> 从存取无序变为存取有序的集合
LinkedHashMap<K,V>/LinkedHashSet<E> 分别是HashMap和HashSet的子类
TreeMap<K,V>
TreeMap<K,V> : 底层为红黑树结构的Map集合实现
红黑树结构: 去重,排序(存取无序),查询效率
注意 : 有排序功能就需要提供排序的规则 -> 绑定比较器和独立比较器
TreeMap集合是双列集合,如果要提供排序规则,提供键集合元素的排序规则即可 -> 夫唱妇随
TreeMap中带有独立比较器的构造方法:
TreeMap(Comparator<K> comparator)
Hashtable<K,V>
Hashtable<K,V> 最早的双列集合,1.2版本并入到集合体系被HashMap替代.
Hashtable<K,V>他是完美的hash表实现!!
Hashtable<K,V>是线程安全的 --> 效率低(悲观锁)
在线程安全的情况下 建议使用HashMap
在线程不安全的情况下 建议使用ConcurrentHashMap -> 悲观锁-> 同步
Hashtable<K,V> 的子类-> Properties 属性集集合 使用非常广泛!!
泛型
泛型 : 一种说不清楚的引用类型
泛型的格式:<字母>
字母:
1. 强烈建议大写,不建议小写-> 不规范
2. 强烈建议单个字母,不建议多个字母 -> 不规范
3. 一个尖括号内可以写多个泛型 中间使用 逗号 分隔 -> <K,V>
泛型的使用:
在具体使用类的时候,拿具体的引用数据类型类名去替换字母即可
如果有泛型存在而使用时不理会泛型,泛型默认是Object类型!!
泛型类
泛型类 : 拥有泛型类型的类叫泛型类 ->例如 public class ArrayList<E>{}
格式:
public class 类名<字母>{
}
作用: 如果在类上定义了泛型类型,那么就代表在此类中 多了一种泛型类型可以直接使用!
在何时确定这个泛型类型的具体类型呢 ? -> 当你使用这个类的时候需要给明泛型的具体类型
在类上定义泛型,此泛型的作用域在 整个类中有效!
泛型方法
泛型方法 : 拥有泛型类型的方法叫泛型方法
格式:
权限修饰符 状态修饰符 <字母> 返回值类型 方法名(形参列表){
方法体;
}
当你在方法上定义泛型类型,从此在方法内就多了一个不明确引用数据类型 给你使用!
具体等到调用方法时明确这个泛型类型!
在方法上定义泛型,此泛型的作用域在 整个方法中有效!
注意方法的泛型 一般都会在形参上使用!!
public static <T> boolean addAll(Collection<T> c, T... elements){方法体}
泛型接口
泛型接口 : 拥有泛型类型的接口叫泛型接口 ->例如 public interface List<E>{}
格式:
public interface 接口名<字母>{
}
泛型接口的使用和泛型类一致!!
泛型的沿用 : 当子类实现父接口/子类继承父类的时候,如果不沿用父类/父接口的泛型,那么从子类开始就没有泛型使用了
泛型的通配符和上限下限(重点)
泛型的通配符 : 一般用在方法的形参上
格式 :<?>
泛型的上限和下限
上限 : <? extends 类型> 传入的泛型必须是后方类型 同类型 或者 子类型
下限 : <? super 类型> 传入的泛型必须是后方类型 同类型 或者 父类型
不管上限还是下限都包含自己这个类型!!