1. 什么是 Map
- API文档描述:将键映射到值的对象(key-value)。一个映射(一个Map对象)不能包含重复的键;效率高
- 键(可以) : 本质也是一个数据(不可以重复)。key 通过 hashcode、equals 判断是否重复。
- 值(value) : 本质也是一个数据(可重复)
- 键值对: 就是一对(和两个是有区别的),一对一般是有某种关系
- 映射 : 上面说的一个映射就看成是一个Map对象 key对应value是一种映射关系
- Object put(Object key,Object value): 如果当前添加的这个key已经存在了,会覆盖这个键之前的值,返回的是之前的value值。
- boolean containsKey(Object key):判断Map集合中是否包含当前的传入的key。
- boolean containsValue(Object value) : 判断Map集合中是否包含当前的传入的value。
- Object get(Object key) : 根据指定的键key找到对应的值,如果没有找到返回 null。
- remove(Object key) : 删除指定key对应的那一对。
重要方法列表:
- Collection values() : 返回一个Map中的所有的value值 Collection集合。
- Set keySet() :返回所有的key值的集合(key是唯一的,所以返回的是Set集合)。
- Set entrySet(): 返回所有的entry对象(key是唯一的,所以返回的是Set集合)。(底层将 key、vaule 封装为Entry 对象。)
问题:为什么 values() 方法返回的是 Collection,而keySe() 、entrySet() 返回的是Set?
Key entry 都不能重复的-Set
2. HashMap (无序)
- 允许 key 、value 值为null,但只允许 一个null 值的键
- hashMap 是线程不安全的。
- 默认容量 为 16。
- hashMap 是如何扩容的?当实际值 等于 容量的 百分之75 扩容。
- HashMap 是无序的:放进去的数据和取出来的数据不一样。
- 超高的访问速度。
2.1. HashMap 原理(面试题)
1. HashMap 是键值对的,它怎么存数据的?
将 key 和 value 封装成对象 Entry
2. 每一次存进去的值放在数组的那个位置?
先计算 key 的Hash值 ②Hash % 数组.length
3. 如果key 计算出来的 Hash 最后 % 的位置一致怎么办?
Hash冲突
链表(拉链法),解决hash冲突。
- 当 链表的结构 > 8 时,链表结构 变为 红黑树。
- 如果数节点 < 6 又从数结构转为链表
4. HashMap 底层是什么?
数组+ 链表 +红黑树
3. LinkedHashMap(有序)
- 有序的,线程不安全的
- LinkedHashMap 通过维护一个额外的双向链表保证了迭代顺序。(LinkedHashMap = HashMap + 双向链表)
4. HashTable(无序)
- key 不允许为null,value 不允许 为null
- HashTable 的 线程安全的,但是效率低。
4.1. Properties(重点)(线程安全)
Properties 类是 Java 中用于处理配置文件的工具类,它继承自 Hashtable 类,实现了 Map 接口。
- Properties 类表示了一个持久的属性集 。
- properties 可以保存到流中 或 从流中加载。
- key、values 存储的类型--String (属性列表中的每个键及其对应的值都是一个字符串)。
4.1.1. Properties 作用
使用该类可以更好的处理文件:
- 配置文件管理: 主要用于读取和保存应用程序的配置信息,例如数据库连接信息、用户设置等。
- 国际化: 用于加载不同语言的资源文件,方便国际化应用程序。
- 持久化: 可以将配置信息持久化到文件,以便下次程序启动时重新加载
4.1.2. Properties 重要方法
1. String getProperty(String key) :使用此属性列表中指定的键搜索属性。
2. String getProperty(String key, String defaultValue) :使用此属性列表中指定的键搜索属性。
3. void list(PrintStream out) : 将此属性列表打印到指定的输出流。
void list(PrintWriter out) :将此属性列表打印到指定的输出流。
4. void load(Reader reader) :以简单的线性格式从输入字符流读取属性列表(关键字和元素对)。
void load(InputStream inStream) :从输入字节流读取属性列表(键和元素对)。
5. Object setProperty(String key, String value) : 设置 键值对。
4.1.3. Properties 的应用场景(读写资源文件)
1. Properties 中 通过 list 方法将值 持久化到文件中。
Properties pp = new Properties();
pp.setProperty("XXXX", "OOOO");
pp.setProperty("YYYY", "JJJJ");
PrintStream ps = new PrintStream("F:/qq.txt");
pp.list(ps);
2. Properties 中 通过 load 将文件中的 值(String类型键值对)加载到 Properties 对象中
Properties p1 = new Properties();
FileInputStream fis = new FileInputStream("D:/qq1.txt");
p1.load(fis);
5. TreedMap(排序)
根据key排序。
5.1. 自然排序
类实现 comparable 接口重写 compareTo 方法
5.2. 定制排序
自定义一个类实现Comparator接口,重写 compare() 方法。
5.3. ConcurrentHashMap
现在安全的Map 的用 ConcurrentHashMap
考虑线程安全或者写入速度的话,可以使用 HashTable,JDK8后建议使用 ConcurrentHashMap替代HashTable,既能获取超高的访问速度,又能保证线程安全
6. Collections
7. 泛型
7.1. 泛型的认识
- 泛型的 本质是 参数化类型,也就是说所操作的 数据类型被指定为一个参数。
- 这种参数类型可以用在 类、接口 和 方法的创建中;分别被称为 泛型类、泛型接口、泛型方法。
简单的理解:就是在设计的类、接口 等 的时候,没有规定具体是什么类型的参数,在实例化 或调用的时候再 传入具体的类型,告诉编译期这是 什么类型。
注意:
- 泛型 不会影响 程序的运行的速度,因为在编译期就会直接编译成具体的类型。
- 使用泛型的方法或实例化泛型的类,只能传入引用数据类型,不能传入基本数据类型。
7.2. 规则限制
- 泛型的类型参数 只能是 引用数据类型,不能是基本数据类型。
- 同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。
- 泛型的类型参数可以有多个。
规范的写法应该是使用一些字母(常见的字母):
E Element 元素
T Type 类型
K Key 键
V Value 值
7.3. 泛型的作用
在编译的时候 检查 类型安全,并且所有的 强制转换都是自动和隐式的,以提高代码的重用率
7.4. 范式类
格式
修饰符 class 类名<类型>{ }
/**
* 设计一个类 表示 飞机大战的地图
*/
public class MapPoint {
int x;
int y;
}
/**
* 设计一个类 表示 世界地图
*/
class MapPoint1 {
BigDecimal x;
BigDecimal y;
}
/**
* 设计一个类 表示 平面坐标
*/
class MapPoint2 {
double x;
double y;
}
class Point<T1,T2> {
T1 x;
T2 y;
}
class Test{
public static void main(String[] args) {
Point<Integer,Character> point1 = new Point<Integer,Character>();
Point<User,Dog> point2 = new Point<User,Dog>();
}
}
7.5. 泛型方法
格式:
格式:修饰符<类型> 返回值类型 方法名(类型 变量名){ };
例:设计一个方法 将2个数组 合并 并排序
public class GenericMethod {
public static void main(String[] args) {
Integer[] a1 = {3,1,2,5,8,4};
Integer[] a2 = {9,2,6};
merge(a1,a2);
}
// 设计一个方法 将2个数组 合并 并排序
// 泛型是在什么时候被确定的? 创建对象 和 调用的时候才能被 确定
// 告诉编译器 这是一个 泛型方法 在返回值前 写上 <T> 表示这个是一个 泛型方法
public static <T> void merge(T[] arr1,T[] arr2){
// 泛型不能实例化 泛型是不能new 对象的
// T[] newArr = new T[arr1.length + arr2.length];
// 创建新数组 并 将arr1的元素 塞进去
T[] ts = Arrays.copyOf(arr1, arr1.length + arr2.length);
// 将arr2的数据塞进去
// 源 源位置 目标 目标位置 长度
System.arraycopy(arr2,0,ts,arr1.length,arr2.length);
Arrays.sort(ts);
System.out.println(Arrays.toString(ts));
}
}
7.6. 泛型的上下限
7.6.1. 设置泛型的上限
<? extends 类型>
例:List <? extends Number>: 它表示List集合中 元素的类型是 Number或者 其子类型
7.6.2. 设置泛型的下限
<? super 类型>
例:List<? super Number> :它表示List集合中 元素的类型是 Number 或者 其父类型