目录
Map
双列集合的特点
Map双列集合体系简图
Map集合的常见API
Map集合的遍历方式
HashMap
LinkedHashMap
TreeMap
Map
双列集合的特点
- 一次可以添加两个(一对)元素,分别为键和值;
- 键不可以重复;值可以重复;
- 键和值一一对应,一个键只能找到自己对应的一个值;
- 键和值的整体称为键值对,也称为键值对对象,Entry。
Map双列集合体系简图
Map集合的常见API
Map式双列集合的顶层接口,它的功能是全部双列集合都可以继承使用的
方法名称 | 说明 |
public V put(K key, V value) | 添加元素 |
public V remove(Object key) | 根据键删除键值对元素 |
public void clear() | 移除所有的键值对元素 |
public boolean containsKey(Object key) | 判断集合是否包含指定的键 |
public boolean containsValue(Object value) | 判断集合是否包含指定的值 |
public boolean isEmpty() | 判断集合是否为空 |
public int size() | 集合的长度,也就是集合中键值对的个数 |
public V get(Object key) | 根据传入的键返回对应的值 |
因为Map是双列集合的顶层接口,不能直接实例化对象,要通过它底下的实现类。
//导包
import java.util.HashMap;
import java.util.Map;
.
.
.
//实例化
Map<String, String> m = new HashMap<>();
1、public V put(K key, V value)
put方法的细节:添加 + 覆盖;
在添加数据的时候,如果键不存在,那么直接把键值对对象添加到Map中,返回null。
在添加数据的时候,如果键存在,那么会把原有的键值对对象覆盖,把原来的值返回。
m.put("aaa","bbb");
m.put("ccc","ddd");
System.out.println(m.put("aaa","eee"));//bbb
//键"aaa"已存在,所以新的键值"aaa","eee"对覆盖了原来的键值对,并返回原来的值"bbb"
System.out.println(m);//{aaa=eee, ccc=ddd}
System.out.println(m.put("fff","ggg"));//null
//键"fff"不存在,直接存入m集合,返回null空串;
2、public V remove(Object key)
要删除的键存在的话,就从Map集合中删除该键值对,并返回该键值对的值。
要删除的键不存在的话,就直接返回null。
System.out.println(m.remove("fff"));//ggg
System.out.println(m.remove("ooo"));//null
3、public void clear()
清空集合中所有键值对。
4、public boolean containsKey(Object key)
判断集合是否包含指定的键。
System.out.println(m.containsKey("aaa"));//true
System.out.println(m.containsKey("eee"));//false
5、public boolean containsValue(Object value)
判断集合是否包含指定的值。
System.out.println(m.containsValue("bbb"));//false
//被覆盖值也不算作存在
System.out.println(m.containsValue("eee"));//true
6、public boolean isEmpty()
判断集合是否为空。
7、public int size()
集合的长度,也就是集合中键值对的个数。
8、public V get(Object key)
根据传入的键返回对应的值。
补充:
getOrDefault() 方法获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值。
hashmap.getOrDefault(Object key, V defaultValue)
通常计数是需要将第一次出现的键对应的值设为1,将重复出现的键对应的值加一;
可以这样写:(假设值的类型是Integer)
hashmap.put(key, hashmap.getOrDefault(key, 0) + 1);
解释:
如果键已经存在,那么返回它对应的值后加一,再通过put覆盖原来的键值对;
如果键不存在,那么返回在该函数中设定的默认值,返回了0,0 + 1之后将结果1 和key的键值对 通过put存入哈希表。
Map集合的遍历方式
键找值
- 获取所有的键放在单列集合里;keySet()方法;
//导包
import java.util.Set;
Set<String> set = m.keySet();
- 遍历单列集合(增强for,迭代器,forEach—Lambda表达式),找对应的值。get()方法
set.forEach(s -> System.out.println(s + "=" + m.get(s)));
//通过Lambda表达式遍历set
键值对
- 通过一个方法获取所有键值对对象,返回一个Set集合;entrySet()方法
//写法一
Set<Map.Entry<String, String>> set1 = m.entrySet();
//写法二
//导包
import java.util.Map.Entry;
.
.
.
Set<Entry<String, String>> set1 = m.entrySet();
Entry是Map的一个内部接口,表示键值对类型,泛型嵌套。
- 遍历entrySet()方法生成的Set集合,用getKey()和getValue()方法得到键和值。
Iterator<Map.Entry<String, String>> it = set1.iterator();
while(it.hasNext()){
Map.Entry<String, String> kv = it.next();
System.out.println(kv.getKey() + "=" + kv.getValue());
}
//迭代器
getKey()和getValue()方法是Entry特有的方法。
键值对的遍历还可以这样写,简化:
for (Map.Entry<String, Integer> entry:hm.entrySet()
) {
System.out.println(entry.getKey() + " = " + entry.getValue());
}
Lambda表达式
方法名 | 说明 |
default void forEach(BiConsumer<? super K, ? super V> action) | 结合Lambda表达式遍历Map集合 |
这是Map中的一个方法,底层还是用增强for实现的。
m.forEach(new BiConsumer<String, String> (){
@Override
public void accept(String key, String value) {
System.out.println(key + "=" + value);
}
});
这是使用forEach()方法时完整的匿名内部类;
如果写完整的匿名内部类还不能忘了导包;
//导包
import java.util.function.BiConsumer;
m.forEach((key, value)-> System.out.println(key + "=" + value));
这是改写成Lambda表达式的形式。
HashMap
特点及原理
- HashMap是Map里的一个实现类
- 没有额外的方法需要学习,可以直接使用Map里的方法
- 特点都是由键决定的:无序、不重复、无索引
- HashMap根HashSet底层原理是一模一样的,都是哈希表结构
使用HashMap时创建了一个数组,通过计算键的哈希值得到应存入的下标;hashCode()
——如果下标处没有数据,则将键值对直接存入;
——如果小标处已经有数据了,则调用equals()方法,比较键的属性值;
————如果键是一样的就会覆盖原来的键值对;
————如果键不一样会直接添加到旧的键值对的下面,形成链表;
当有链表长度超过8且数组长度大于等于64,会将链表自动转成红黑树;
小结:
- HashMap底层时哈希表结构的;
- 以来hashCode方法和equals方法保证键的唯一;
- 如果键储存的是自定义对象,需要重写hashCode和equals方法;如果值储存的是自定义对象,不需要重写hashCode方法和equals方法;(因为哈希表根据键来判断应存入的位置)。
注意重写hashCode和equals方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
练习
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class HashMapDomo02 {
public static void main(String[] args) {
//模拟投票
//80个学生投票4个景点A,B,C,D
//每人投一票,统计最多票数的景点
//需要先让学生投票
//定义一个数组,保存4个节点
String[] arr = new String[]{"A","B","C","D"};
HashMap<String,Integer> hm = new HashMap<>();
Random random = new Random();
for(int i = 0;i < 80;i++){
int index = random.nextInt(arr.length);//生成范围 0 ~ 3;0 ~ arr.length - 1
//统计 如果景点已存在,值加一 如果景点不存在,存入景点,值为1
if(hm.containsKey(arr[index])){
hm.put(arr[index], hm.get(arr[index]) + 1);
continue;
}
hm.put(arr[index], 1);
}
int max = Integer.MIN_VALUE;
String ans = "";
for (Map.Entry<String, Integer> entry:hm.entrySet()
) {
if(entry.getValue() > max){
max = entry.getValue();
ans = entry.getKey();
System.out.println(entry.getKey() + " = " + entry.getValue());
}
}
System.out.println(ans);//D
}
}
LinkedHashMap
特点
由键决定:有序(链表存储顺序)、不重复、无索引
是HashMap的子类
这里的有序指的是保证存储和去除的顺序一致
原理
底层数据结构依然是哈希表,只是每个键值对元素有额外多了一个双链表的机制记录存储的顺序;
与LinkedHashSet相同,唯一不同的是 如果将要添加的键值对的键在哈希表中已经存在,那么会覆盖该键值对,而不是舍弃新的数据。
TreeMap
特点
由键决定特性:不重复、无索引、可排序
可排序指对键进行排序。
排序默认按照键的从小到大进行排序,也可以自己规定键的排序规则。
原理
TreeMap根TreeSet底层原理一样,都是红黑树结构,唯一不同的是 存入元素已存在会覆盖、更新键值对;
两种排序规则
规则一
实现Comparable接口,指定比较规则;
class Student2 implements Comparable<Student2>{
private String name;
private int age;
.
.
.
@Override
public int compareTo(Student2 o) {
//按照学生年龄升序排序,如果年龄相同,按照姓名排序,如果行名相同认为是同一人
int i = this.getAge() - o.getAge();
i = i == 0 ? this.getName().compareTo(o.getName()) : i;
return i;
}
}
注意:
this:表示当前要添加的元素
o:表示已经在红黑树中的元素
返回值:
负数:表示当前要添加的元素小,存左边;
正数:表示当前要添加的元素大,存右边;
0:表示当前要添加的元素已经存在,覆盖;
规则二
创建集合时传递Comparator比较器对象,指定比较规则。
//导包
import java.util.Comparator;
.
.
.
TreeMap<Integer, String> tm2 = new TreeMap<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
利用Lambda表达式简化
TreeMap<Integer, String> tm2 = new TreeMap<>((o1, o2) -> {return o2 - o1;});
如果两种排序方式都写了,那么以第二种排序方式为准。