目录

Map

双列集合的特点

Map双列集合体系简图

Map集合的常见API

Map集合的遍历方式

HashMap

LinkedHashMap

TreeMap

Map

双列集合的特点

  1. 一次可以添加两个(一对)元素,分别为键和值;
  2. 键不可以重复;值可以重复;
  3. 键和值一一对应,一个键只能找到自己对应的一个值;
  4. 键和值的整体称为键值对,也称为键值对对象,Entry。

Map双列集合体系简图

android 使用map反射混淆后报错_System

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集合的遍历方式

键找值

  1. 获取所有的键放在单列集合里;keySet()方法;
//导包
import java.util.Set;

android 使用map反射混淆后报错_java_02

Set<String> set = m.keySet();
  1. 遍历单列集合(增强for,迭代器,forEach—Lambda表达式),找对应的值。get()方法
set.forEach(s -> System.out.println(s + "=" + m.get(s)));
//通过Lambda表达式遍历set

键值对

  1. 通过一个方法获取所有键值对对象,返回一个Set集合;entrySet()方法
//写法一
Set<Map.Entry<String, String>> set1 = m.entrySet();
//写法二
//导包
import java.util.Map.Entry;
.
.
.
Set<Entry<String, String>> set1 = m.entrySet();

Entry是Map的一个内部接口,表示键值对类型,泛型嵌套。

  1. 遍历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

特点及原理

  1. HashMap是Map里的一个实现类
  2. 没有额外的方法需要学习,可以直接使用Map里的方法
  3. 特点都是由键决定的:无序、不重复、无索引
  4. HashMap根HashSet底层原理是一模一样的,都是哈希表结构

使用HashMap时创建了一个数组,通过计算键的哈希值得到应存入的下标;hashCode()

——如果下标处没有数据,则将键值对直接存入;

——如果小标处已经有数据了,则调用equals()方法,比较键的属性值;

————如果键是一样的就会覆盖原来的键值对;

————如果键不一样会直接添加到旧的键值对的下面,形成链表;

当有链表长度超过8且数组长度大于等于64,会将链表自动转成红黑树;

小结:

  1. HashMap底层时哈希表结构的;
  2. 以来hashCode方法和equals方法保证键的唯一
  3. 如果储存的是自定义对象,需要重写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;});

如果两种排序方式都写了,那么以第二种排序方式为准。