Map接口
Map接口适合存储键值对组合的数据。
Map<K,V>
泛型K:键的类型
泛型V: 值的类型
HashMap类
HashMap类是属于Map接口的实现类。无序;
基本使用
构造方法:
HashMap<K,V>
泛型K:键的类型
泛型V: 值的类型
K - 由该地图维护的键的类型
V - 映射值的类型
HashMap()
构造一个空的 HashMap ,默认初始容量(16)和默认负载系数(0.75)。
方法:
values()
作用:获取集合中所有的值。
参数:无;
返回值:Collect对象
示例:
// 创建HashMap对象
HashMap<String,String> hashMap = new HashMap<String,String>();
// 添加元素
hashMap.put("aqua", "44.5");
hashMap.put("yousa", "当春乃发生");
hashMap.put("Reine", "北京站");
// 获取所有值 无序的
Collection<String> values = hashMap.values();
System.out.println("values = " + values); // values = [当春乃发生, 44.5, 北京站]
size()
作用:返回元素个数。
参数:无;
返回值:int类型
示例:
// 创建HashMap对象
HashMap<String,String> hashMap = new HashMap<String,String>();
// 添加元素
hashMap.put("aqua", "44.5");
hashMap.put("yousa", "当春乃发生");
hashMap.put("Reine", "北京站");
// size方法
int size = hashMap.size();
System.out.println("size = " + size); // size = 3
replace()
作用:替换元素。
参数:键值(新值)对;
返回值:值的类型
示例:
// 创建HashMap对象
HashMap<String,String> hashMap = new HashMap<String,String>();
// 添加元素
hashMap.put("aqua", "44.5");
hashMap.put("yousa", "当春乃发生");
hashMap.put("Reine", "兔次元");
// replact替换
String replaceItem = hashMap.replace("Reine", "油菜花");
System.out.println("replaceItem = " + replaceItem); // 之前的 值的内容
remove(Object key)
作用:删除元素。
参数:键;
返回值:null或删除的键的值
示例:
// 创建HashMap对象
HashMap<String,String> hashMap = new HashMap<String,String>();
// 添加元素
hashMap.put("aqua", "44.5");
hashMap.put("yousa", "当春乃发生");
hashMap.put("Reine", "兔次元");
// remove 删除
String rmItem = hashMap.remove("Reine");
System.out.println("rmItem = " + rmItem); // 键不存在时 返回 null,健存在时返回 键的值
put()
作用:向Map集合中添加元素。
参数:键值对;
返回值:无
示例:
// 创建HashMap对象
HashMap<String,String> hashMap = new HashMap<String,String>();
// 添加元素
hashMap.put("aqua", "44.5");
keySet()
作用:获取所有的键的组合。
参数:无;
返回值:Set类型对象,每个对象的类型是通过泛型控制的
示例:
// 创建HashMap对象
HashMap<String,String> hashMap = new HashMap<String,String>();
// 添加元素
hashMap.put("aqua", "44.5");
hashMap.put("yousa", "当春乃发生");
hashMap.put("Reine", "兔次元");
// 获取键的值
Set<String> keysSet = hashMap.keySet();
System.out.println("keysSet = " + keysSet);
isEmpty()
作用:判断集合是否为空。
参数:无;
返回值:布尔值
示例:
// 创建HashMap对象
HashMap<String,String> hashMap = new HashMap<String,String>();
// 添加元素
hashMap.put("aqua", "44.5");
hashMap.put("yousa", "当春乃发生");
hashMap.put("Reine", "兔次元");
// 判断hashMap是否为空
boolean isempty = hashMap.isEmpty();
System.out.println("isempty = " + isempty); //false
get(Object key)
作用:获取hashMap中的某一个元素的值。
参数:key;
返回值:值的类型 Object
示例:
// 创建HashMap对象
HashMap<String,String> hashMap = new HashMap<String,String>();
// 添加元素
hashMap.put("aqua", "44.5");
hashMap.put("yousa", "当春乃发生");
hashMap.put("Reine", "兔次元");
// 获取元素的值
String aqua = hashMap.get("aqua");
System.out.println("aqua = " + aqua); // aqua = 44.5
entrySet()
作用:获取所有键值对的组合。
参数:无;
返回值:值的类型 Object
示例:
// 创建HashMap对象
HashMap<String,String> hashMap = new HashMap<String,String>();
// 添加元素
hashMap.put("aqua", "44.5");
hashMap.put("yousa", "当春乃发生");
hashMap.put("Reine", "兔次元");
// 获取所有键值对的组合
// Set类型的对象,这个对象的类型是 Entry 类型(键值对)(他的键值类型泛型约束为String),entries是键值对类型元素组成数组形式的对象
Set<Map.Entry<String, String>> entries = hashMap.entrySet();
System.out.println("entries = " + entries); // entries = [yousa=当春乃发生, aqua=44.5]
Map.Entry的理解
Entry翻译过来是条目,项的意思。
Entry是一个接口(类型),在Map接口内部又写了一个接口(内部类形式);
Map.Entry<String, String> Map.Entry代表的是Entry接口是在Map接口的内部接口;
同时hashMap类中的Node类是Entry接口的实现类;
每一个hashMap对象中的元素Node(键值对)是Entry接口的实现类;
在 hashMap 类 源码
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
...
}
containsValue(Object value)
作用:是否包含某个值。
参数:值的值;
返回值:布尔值
示例:
// 创建HashMap对象
HashMap<String,String> hashMap = new HashMap<String,String>();
// 添加元素
hashMap.put("aqua", "44.5");
hashMap.put("yousa", "当春乃发生");
hashMap.put("Reine", "兔次元");
// 是否包含某个值
boolean res = hashMap.containsValue("44.5");
System.out.println("res = " + res); // res = true
containKey()
作用:是否包含某个键。
参数:键的名字;
返回值:布尔值
示例:
// 创建HashMap对象
HashMap<String,String> hashMap = new HashMap<String,String>();
// 添加元素
hashMap.put("aqua", "44.5");
hashMap.put("yousa", "当春乃发生");
hashMap.put("Reine", "兔次元");
// 是否包含某个键
boolean res2 = hashMap.containsKey("aqua");
System.out.println("res2 = " + res2); // res2 = true
clear()
作用:清空集合。
参数:无;
返回值:无
示例:
// 创建HashMap对象
HashMap<String,String> hashMap = new HashMap<String,String>();
// 添加元素
hashMap.put("aqua", "44.5");
hashMap.put("yousa", "当春乃发生");
hashMap.put("Reine", "兔次元");
// 清空集合
hashMap.clear();
// 获取所有键值对的组合
Set<Map.Entry<String, String>> entries = hashMap.entrySet();
System.out.println("entries = " + entries); // entries = []
集合遍历
方式1:获取所有的键,再根据键获取值,获取键值对
// 方式1:获取所有的键 根据键获取值
Set<String> keyList = hashMap.keySet();
// set集合没有下标,不能使用普通for循环,需要使用迭代器
for (String item : keyList) {
// 根据key获取value
String value = hashMap.get(item);
// 打印 key value
System.out.println(item + " == " + value);
}
方式2:遍历所有的值,获取键
// 方式2:遍历所有的值
// 获取所有值
Collection<String> valList = hashMap.values();
// 遍历所有值
for (String val : valList) {
System.out.println("val = " + val);
}
方式3:通过entrySet方法获取键值对列表
// 方式3:获取所有的键值对的组合
// 获取所有的键值对列表,每一个Node(键值对)就是 entry
Set<Map.Entry<String, String>> entries = hashMap.entrySet();
// 遍历 entries对象 ,entry就是每个键值对元素
for (Map.Entry<String, String> entry : entries) {
// 获取键 entry.getKey() 使用的是 Entry接口中的方法
String key = entry.getKey();
// 获取值 entry.getValue() 使用的是 Entry接口中的方法
String value = entry.getValue();
System.out.println(key + " == " + value);
}
方式4:通过获取所有的键的迭代器,获取键值对
// 方式4:获取所有的键的迭代器
// 获取 所有键的 迭代器对象
Iterator<String> iterator = hashMap.keySet().iterator();
// 遍历迭代器对象
while (iterator.hasNext()){
String key = iterator.next();
String value = hashMap.get(key);
System.out.println(key + " == " + value);
}
方式5:通过获取所有的值的迭代器,获取值
// 方式5:获取所有的值的迭代器
// 获取 所有值的 迭代器对象
Iterator<String> iterator = hashMap.values().iterator();
// 遍历迭代器对象
while (iterator.hasNext()){
String value = iterator.next();
System.out.println(value);
}
方式6:通过获取所有的键值对组合的迭代器,获取键值对
// 方式6 :
// 获取所有的键值对组合的迭代器
Iterator<Map.Entry<String, String>> iterator1 = hashMap.entrySet().iterator();
// 遍历迭代器对象
while (iterator1.hasNext()){
// 获取键值对
Map.Entry<String, String> entry = iterator1.next();
// hashMap类中的Node类重写了 toString方法
// static class Node<K,V> implements Map.Entry<K,V> {
System.out.println(entry);
}
查看谁实现类
集合特点
键不允许重复,重复的键会把值覆盖;
无序;
没有下标;
允许null的键和null的值;
线程不安全;
增删改查效率,通常情况下是比较高的(先在数组中查,然后再在链表中查);
数据结构
hashMap数据结构:
JDK1.7之前的是 数组+单项链表
JDK1.8是 数组+单项链表+红黑树(默认)
HashMap中的每个元素是一个Node对象 属于Entry接口的实现类 所以我们也称HashMap中的元素为Entry对象;
每个Node对象中包含四部分(四个属性):key值、value值、根据key计算出来的hash值、下一个元素的引用next;
单项链表
单向链表中的每一个Node是由 item 和 next 组成,item保存的是自己,next指向下一个元素。
单项链表只能从一个方向查找,从前往后查找。
红黑树:
普通二叉树如图所示;当左侧的节点小于中间的节点,右侧的节点大于中间的节点叫作二叉查找树;当数值都大于中间节点时,就会变成一个单向链表(不平衡)查找效率低;于是出现了二叉平衡查找树,在插入元素的时候会进行排序,若各个层级之间的高度差超过1,则进行旋转,最终变成一个平衡二叉查找树;然而平衡二叉查找树需要不停的旋转(移动元素)以维持高度差不超过1,浪费了时间,于是又进行了改进,出现了红黑树;红黑树允许高度差大于1,以减少(旋转)移动元素的次数,来减少时间,提高效率,红黑树也属于平衡二叉查找树,但不要求严格的平衡,是不完全平衡的二叉查找树。
红黑树规则特点(了解)
1.每个节点只能是红色或者黑色。
2.根节点必须是黑色。
3.红色的节点,它的叶节点只能是黑色。
4.从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。
由以上四个特性我们可以看出一些红黑树的特点:
从根基节点到最远叶节点的路径(最远路径)肯定不会大于根基节点到最近节点的路径(最短路径)的两倍长。
这是因为性质3保证了没有相连的红色节点,性质4保证了从这个节点出发的不管是哪一条路径必须有相同数目的黑色节点,这也保证了有一条路径不能比其他任意一条路径的两倍还要长。
HashMap添加元素的过程:
1、使用HashMap的构造函数创建对象时,在第一次添加元素时,Java虚拟机会在底层维护一个长度为16的数组;
2、**(重要)**当添加元素时,hashMap类中的方法会把(元素Node中的hash值)和(数组的长度减1)进行与运算, 得到一个 0-15的整数的值作为下标n;判断下标为n位置是否存在元素,若没有则把这个元素存放在下标为n的位置;若下标为n位置存在元素,则先判断这个元素的key值以及hashCode值是否相同,若都相同则覆盖,若不同判断当前节点为树节点还是链表节点;如果为树节点 则添加在树中;如果为链表 则添加在链表的末尾;
hash值 % length = hash &(length -1); // 目的是为了得到一个 0-15 的整数的值
3、当链表的长度大于8 并且数组的长度大于64 将单向链表转换为一个红黑树(JDK1.8);转换红黑树机制
4、**(重要)**当数组的使用率(元素占用了3/4数组长度)达到了75%,数组的长度就会扩容到原来容量的2倍;HashMap扩容机制
5、当链表的元素个数小于6 将红黑树转换为单向链表;取消树化的机制
网址
https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
AVL Trees (Balanced binary search trees) 平衡二叉查找树
JDK8中支持红黑树,但是数组 + 单向链表依然是HashMap中的数据结构常态。
原因:转化为红黑树的概率是非常低的,低于千万分之一。
- 当数组的使用率(元素占用了3/4数组长度)达到了75%,数组的长度就会扩容到原来容量的2倍。
- 扩容后数组中的元素还会重新排列,排列规则为继续使用hash值对新的长度-1进行&与运算。
- 最终有两种结果:1、继续保存在原来的位置;2、移动到原来的位置加上扩容的长度的位置;
源码解析
无参构造的源码,创建一个loadFactor(负载因子:默认0.75,达到扩容的阈值);
static final float DEFAULT_LOAD_FACTOR = 0.75f;
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
添加元素过程(重要)
put方法中,计算hash值,创建一个Node类型(Node类实现了Map.Entry接口)的数组;
初始化一个Node元素;resize()方法就是一个扩容的方法,创建一个长度为16的数组;
进行与运算判断,进而判断是否是树节点还是链表节点,然后进行添加元素;
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
...
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
...
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
扩容机制(重要)
resize()扩容方法中,当数组的使用率大于数组长度的75%,就进行扩容,扩容2倍。扩容后会对数组中的元素进行重新排列,排列规则为继续使用hash值对数组长度-1进行与运算,得到的结果为要不还是存在老的位置,要不存老位置+扩容的数量新位置,只存放数组中的元素,数组中元素的下边链表(或红黑树)自然跟着过来,链表不发生改变。
静态常量
// 单向链表超过 8 个元素时,会转为红黑树
static final int TREEIFY_THRESHOLD = 8;
// 红黑树的元素小于 6个 时,转为单向链表
static final int UNTREEIFY_THRESHOLD = 6;
// 最大hashMap元素的数量为 2的 30次方,再大就无法扩容存储了
static final int MAXIMUM_CAPACITY = 1 << 30;
// 初始 数组的大小为 16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
// 转为红黑树数组的最小大小为 64
static final int MIN_TREEIFY_CAPACITY = 64;
HashTable类
集合特点
任何非
null
对象都可以用作键值或值;无序;
面试题目:
hashMap和hashTable的区别
1、HashMap使用懒加载思想,当第一次添加元素时,将初始化长度为16的数组;HashTable在调用无参构造即初始化长度为11数组;
2、hashMap是线程不安全的,hashTable是线程安全的;
3、HashMap扩容为2倍,HashTable扩容为2倍+1;
4、HashMap使用与运算获取元素应该存放的数组的下标;HashTable使用取余运算获取元素存放数组的下标(了解);
基本使用
方法和遍历方式和HashMap一致
构造方法
Hashtable()
构造一个新的,空的散列表,默认初始容量(11)和负载因子(0.75)。
方法
put()
作用:向Map集合中添加元素。
参数:键值对;
返回值:无
示例:
// 创建HashTable对象
Hashtable<String,String> hashtable = new Hashtable<>();
// 向HashTable中添加元素
String putItem = hashtable.put("sakuna", "阿夸色");
String putItem2 = hashtable.put("Reine","兔次元");
...
其他的方法和HashMap中使用一致。
源码解析
扩容方面的代码:默认2倍+1;
// 默认数组空间为 11,增长因子为75%
public Hashtable() {
this(11, 0.75f);
}
// 扩容的长度为 2倍 + 1
protected void rehash() {
int oldCapacity = table.length;
Entry<?,?>[] oldMap = table;
// overflow-conscious code
// 扩容的长度为 2倍 + 1
int newCapacity = (oldCapacity << 1) + 1;
if (newCapacity - MAX_ARRAY_SIZE > 0) {
if (oldCapacity == MAX_ARRAY_SIZE)
// Keep running with MAX_ARRAY_SIZE buckets
return;
newCapacity = MAX_ARRAY_SIZE;
}
Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
modCount++;
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
table = newMap;
新增元素的代码:使用取余计算下标,HashMap中的位运算获取下标的方式效率更高。
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table;
int hash = key.hashCode();
// 取余操作
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
addEntry(hash, key, value, index);
return null;
}
Properties类
Properties类继承了HashTable类,HashTable实现了Map接口;是HashMap的子类;
Properties类用于后续读取配置文件信息;
集合特点
Node的类型键值对必须只能是String;
原因:因为继承自Hashtable,所以可以直接访问父类的 put或者 putAll方法;
但是不推荐使用,因为Properties类存在的意义在于,只希望用户添加键以及值都为字符串的数据,应该使用setProperty()方法添加数据。
基本使用
构造方法
// 创建一个没有默认值的空属性列表
Properties()
// 创建具有指定默认值的空属性列表
Properties(Properties defaults)
方法
setProperties()
作用:Properties链表内添加元素
参数:String(键)和String(值)
返回值:布尔值或无
示例:
// 创建一个属性值为空的属性列表
Properties properties = new Properties();
// 向属性列表中添加元素
Object cpu = properties.setProperty("CPU", "i9 9900K");
// System.out.println("cpu = " + cpu); // cpu = null
properties.setProperty("Mem","64GB");
properties.setProperty("HardDrive","16TB");
getProperties()
作用: 根据键获取属性值
参数:属性名(键)
返回值:无
示例:
// 创建一个属性值为空的属性列表
Properties properties = new Properties();
// 向属性列表中添加元素
Object cpu = properties.setProperty("CPU", "i9 9900K");
// System.out.println("cpu = " + cpu); // cpu = null
properties.setProperty("Mem","64GB");
properties.setProperty("HardDrive","16TB");
// 根据键获取值
String mem = properties.getProperty("Mem");
System.out.println("mem = " + mem); // mem = 64GB
list()
作用:将此属性列表打印到指定的输出流
参数:System.out(输出流)
返回值:无
示例:
// 创建一个属性值为空的属性列表
Properties properties = new Properties();
// 向属性列表中添加元素
Object cpu = properties.setProperty("CPU", "i9 9900K");
// System.out.println("cpu = " + cpu); // cpu = null
properties.setProperty("Mem","64GB");
properties.setProperty("HardDrive","16TB");
// 根据键获取值
String mem = properties.getProperty("Mem");
System.out.println("mem = " + mem); // mem = 64GB
// list方法
properties.list(System.out);
load()
作用:暂时用不到 2025-07-24
参数:System.out(输出流)
返回值:布尔值或无
示例:
LinkedHashMap类
LinkedHashMap是一个有序的Map集合,顺序为添加顺序,继承自HashMap。
HashMap中的方法在LinkedHashMap类的实例中都可以使用。
集合特点
有序;
键不能重复,值可以重复;
双向链表;
基本使用
方法:LinkedHashMap继承自HashMap类,方法的使用一致;
put()
作用:向集合中添加元素
参数:键值对
返回值:无
示例:
// 创建LinkedHashMap对象, 设置泛型
LinkedHashMap<String,String> linkedHashMap = new LinkedHashMap<String,String>();
// 向Map集合中添加元素
linkedHashMap.put("Sakuna", "湛蓝依旧");
linkedHashMap.put("yousa","大喜");
linkedHashMap.put("Reine","窃窃");
...
常用方法和HashMap中使用一致。
遍历方式
方式:使用 迭代器或增强for遍历
// 遍历元素,没有下标只能通过迭代器方式,通过键的列表的 Iterator方法
Iterator<String> iterator = linkedHashMap.keySet().iterator();
// 遍历
while(iterator.hasNext()){
String key = iterator.next();
String value = linkedHashMap.get(key);
System.out.println(key + " == " + value);
}
方式2:通过增强for
// 增强for
Set<Map.Entry<String, String>> entries = linkedHashMap.entrySet();
for (Map.Entry<String, String> entry : entries) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + " == " + value);
}
TreeMap类
TreeMap类是有序的Map集合,顺序为根据键比较的顺序;
常用方法和遍历方式与HashMap完全相同;
集合特点
有序;(默认使用无参构造创建的实例根据键进行排序,也可以使用有参构造创建实例,通过自定义比较器进行排序)
基本使用
构造方法
// 使用其键的自然排序构造一个新的空树状图
无参构造:默认使用无参构造创建的实例根据键进行排序
TreeMap()
// 键的类型 是 Student 类型时,如何比较顺序,
在查看源码中的compare方法得知,compare方法首先判断有没有指定比较器,若没有指定比较器则使用Comparable接口的实现类(Student类需要实现Comparable接口,重写比较方法)来进行比较,若指定了自定义比较器,则使用自定义比较器进行比较。
有参构造:使用有参构造创建实例,通过自定义比较器进行排序
TreeMap(Comparator<? super K> comparator)
比较器的规则:
x 当前对象,y传入的对象;
x=y 则 返回 0;
x>y 则返回 1;返回正数即可
x<y 则返回 -1;返回负数即可
示例:使用JDK中Integer类中的重写的方法比较;
// 创建Treemap对象 TreeMap后边的可省略,默认使用无参构造,使用类中重写 Comparable接口的方法
TreeMap<Integer,String> treeMap = new TreeMap<>();
// 添加元素
String sakuna = treeMap.put(18, "sakuna");
// System.out.println("sakuna = " + sakuna); // sakuna = null
treeMap.put(19,"yousa");
treeMap.put(17,"小语");
// 遍历集合
Set<Map.Entry<Integer, String>> entries = treeMap.entrySet();
for (Map.Entry<Integer, String> entry : entries) {
Integer key = entry.getKey();
String value = entry.getValue();
System.out.println(key + " = " + value);
}
示例1:无参构造,使用自定义类重写方法实现比较顺序。
Student类实现Comparable接口,重写比较compareTo方法。
package com.collectPart.TreeMapPart;
// Comparable接口中的需要指定泛型,由于比较的键是对象类型,对象类型为VUP所以,泛型写VUP
public class VUP implements Comparable<VUP> {
private String name;
private int age;
// 封装
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public VUP() {
}
public VUP(String name, int age) {
this.name = name;
this.age = age;
}
// 重写方法
// 重写toString方法
@Override
public String toString() {
return "VUP{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
// 重写compareTo方法
@Override
public int compareTo(VUP vup) {
// 比较规则:
// x 当前对象,y传入的对象;
// x=y 则 返回 0;
// x>y 则返回 1;返回正数即可
// x<y 则返回 -1;返回负数即可
// 方式1:
return vup.getAge() - this.getAge();
// 方式2:
// return this.getAge() > vup.getAge() ? 1 : (this.getAge() < vup.getAge() ? -1 : 0) ;
// 方式3:
// if(this.getAge() > vup.getAge()){ // 当前对象的年龄大于传入对象的年龄
// return 1; // 返回正数
// }else if(this.getAge() < vup.getAge()){ // 当前对象的年龄小于传入对象的年龄
// return -1; // 返回负数
// }
// return 0; // 以上条件都不成立 表示两个对象的年龄相等 则 返回0
}
}
public class TestVup {
public static void main(String[] args) {
// 创建三个对象
VUP up1 = new VUP("yousa",19);
VUP up2 = new VUP("Reine",20);
VUP up3 = new VUP("Tori",21);
// 创建TreeMap对象, 指定键值对泛型 为 VUP和String
TreeMap<VUP,String> treeMap = new TreeMap<VUP,String>();
// 添加元素
treeMap.put(up1,"时光洪流");
treeMap.put(up2,"哑谜");
treeMap.put(up3,"桃子桃子桃");
// 遍历Map集合
Set<Map.Entry<VUP, String>> entries = treeMap.entrySet();
for (Map.Entry<VUP, String> entry : entries) {
// 直接打印对象,TreeMap类中重写了 Entry接口中的toString方法
System.out.println(entry);
}
}
}
示例2:有参构造,自定义比较器(自定义一个类),实现Comparator
接口,实现比较顺序。
// 自定义一个类,这个类不重写 Comparable接口
package com.collectPart.TreeMapPart;
public class Singer {
private String name;
private int age;
// 封装
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Singer(String name, int age) {
this.name = name;
this.age = age;
}
// 重写
@Override
public String toString() {
return "Singer{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
// 自定义一个类,重写Comparator接口
package com.collectPart.TreeMapPart;
import java.util.Comparator;
// 实现 Comparator 接口,同时设置泛型为 Singer类型
public class ComparaSinger implements Comparator<Singer> {
@Override
public int compare(Singer o1, Singer o2) {
// o1大于o2 返回正数 o1小于o2 返回负数 o1==o2 返回0
return o1.getAge() > o2.getAge() ? 1 : (o1.getAge() < o2.getAge() ? -1 : 0) ;
}
}
// 测试类
package com.collectPart.TreeMapPart;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
public class TestSinger {
public static void main(String[] args) {
// 创建singer对象
Singer singer1 = new Singer("Reine",23);
Singer singer2 = new Singer("许嵩",39);
Singer singer3 = new Singer("李荣浩",40);
// 创建TreeMap集合,通过有参构造,参数是 ComparaSinger 类的对象
TreeMap<Singer,String> treeMap = new TreeMap<>(new ComparaSinger());
// 向集合中添加元素
treeMap.put(singer1,"窃窃");
treeMap.put(singer2,"有何不可");
treeMap.put(singer3,"年少有为");
// 遍历
Set<Map.Entry<Singer, String>> entries = treeMap.entrySet();
for (Map.Entry<Singer, String> entry : entries) {
System.out.println(entry);
}
}
}
方法
put()
作用:向集合中添加元素
参数:键值对
返回值:无
示例:
// 创建LinkedHashMap对象, 设置泛型
TreeMap<Singer,String> treeMap = new TreeMap<>(new ComparaSinger());
// 向集合中添加元素
treeMap.put(singer1,"窃窃");
treeMap.put(singer2,"有何不可");
treeMap.put(singer3,"年少有为");
...
常用方法和遍历方式与HashMap完全使用相同。
遍历方式
使用增强for遍历
// 创建singer对象
Singer singer1 = new Singer("Reine",23);
Singer singer2 = new Singer("许嵩",39);
Singer singer3 = new Singer("李荣浩",40);
// 创建TreeMap集合,通过有参构造,参数是 ComparaSinger 类的对象
TreeMap<Singer,String> treeMap = new TreeMap<>(new ComparaSinger());
// 向集合中添加元素
treeMap.put(singer1,"窃窃");
treeMap.put(singer2,"有何不可");
treeMap.put(singer3,"年少有为");
// 遍历
Set<Map.Entry<Singer, String>> entries = treeMap.entrySet();
for (Map.Entry<Singer, String> entry : entries) {
System.out.println(entry);
}
面试问题
Comparable接口和Comparator接口的区别
1、Comparable接口属于自然排序 (直接在本类中定义比较规则);Comparator接口属于非自然排序 (需要单独定义类来编写比较规则)
2、如果我们不能直接修改对象类,则需要单独编写比较器类(使用类中的某个属性来比较)来实现对象比较;
如果不能修改比较对象类,则需要单独编写来实现对象比较,自定义比较类实现Comparator接口,使用有参构造创建TreeMap集合实例。