【Java集合】Map接口,及其实现类HashMap、TreeMap详解

继上文的【Java集合】逻辑结构超强、表达十分清晰的“Collection接口”及“List接口、Set接口”解析,本文还将介绍Java集合中的另一种类型:Map类型。

文章目录

  • 集合体系
  • Map接口
    • 方法
    • 迭代
  • HashMap类
  • TreeMap类
    • 具备自然顺序
    • 不具备自然顺序
    • 传入比较器

集合体系

【Java集合】Map接口,及其实现类HashMap、TreeMap详解_第1张图片
Map型的集合与前面两种集合最大的区别就是:

  • Map集合的一个元素是两个数据组成,也即键值对;
  • List集合和Set集合都是一个元素就是单个元素组成;

Map接口

这里是引用

从Collection接口出发理解Map接口:
我们通过查看Map接口描述,发现Map接口下的集合与Collection接口下的集合,它们存储数据的形式不同,如下图。

  • Collection中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方式存储。
  • Map中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的值。
  • Collection中的集合称为单列集合,Map中的集合称为双列集合。
    【Java集合】Map接口,及其实现类HashMap、TreeMap详解_第2张图片

Map容器的特点:

  1. 存储的数据都是以键值对的形式存在的,即Key:Value;
  2. Map中的集合不能包含重复的键(一重复就覆盖),值可以重复;每个键只能对应一个值。

方法

添加:

  1. V put(K key, V value) // 如果之前没有存在该键,那么返回的是null,如果之前就已经存在该键了,那么就返回该键之前对应的值。
  2. void putAll(Map m) //把参数的Map数据全部添加到调用此方法的Map中;

删除

  1. V remove(Object key) //根据键删除一条map中的数据,返回的是该键对应的值。
  2. void clear() //无返回值,删除全部。

获取:

  1. V get(Object key) //根据指定的键获取对应的值
  2. int size() //获取map集合键值对个数

判断:

  1. boolean containsKey(Object key) //判断map集合是否包含指定的键:
  2. Boolean containsValue(Object value) //判断map集合中是否包含指定的值
  3. Boolean isEmpty() //判断是都为空;

**获取键值对:**key只能以set来保存,因为key是不可以重复的;value用collection来保存;

//获取键值对
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Main {
	public static void main(String[] args) {
		//创建Map对象
		Map<String,String> map = new HashMap<String,String>();
		//添加映射关系
		map.put("ITCAST001", "张三");
		map.put("ITCAST002", "李四");
		map.put("ITCAST005", "李四");
		
		//Set keySet() : 以Set的形式获返回所有的key
		Set<String> keys = map.keySet();
		for (String key : keys) {
			System.out.println(key);
		}
		System.out.println("-----------");
		
		//Collection values() :以Collection形式返回所有Value
		Collection<String> values = map.values();
		for (String value : values) {
			System.out.println(value);
		}
	}
}

迭代

迭代:

  1. keySet()方法:以keySet获取所有键,再据键get值
  2. values()方法:把所有的值存储到一个Collection中,再根据值获取键
  3. entrySet()方法:
    a) 把Map集合通过代码Set> entrySet()转换成一个Set集合:Set> entries = maps.entrySet()
    b) 把该个Map中的Key、Value转换为一个整体:entries = [(aa=1000), (bb=10), (cc =10), (dd=100), (ee=30) ]
    c) 此时就可以通过遍历Set集合的方式遍历该个Map
  4. forEach方法:JDK1.8引入的Lambda表达式
//遍历实例
public class Main {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("aa","AAA");
        map.put("bb","BBB");
        map.put("cc","CCC");

        Set<String> keySet = map.keySet();
        Iterator<String> it = keySet.iterator(); //因为Set是Collection,所以可以获取迭代器
        while(it.hasNext()) {
            String key = it.next();
            System.out.println("Key:"+key+"  Value:"+map.get(key));
        }
        for(String key:keySet) { //增强for
            System.out.println("Key:"+key+"  Value:"+map.get(key));
        }

        //values方法
        Collection<String> values = map.values();
        for(String ele:values) {
            System.out.println("Value:"+ele);
        }

        //将Map转换成Set
        Set<Map.Entry<String,String>> set = map.entrySet(); //Entry是一个内部类
        for(Map.Entry<String,String> ele:set) {
            System.out.println("Key:"+ele.getKey() +" Value:"+ele.getValue());
        }

        map.forEach((k,v) -> System.out.println("Key:"+k+"  Value:"+v));
    }
}


HashMap类

HashMap容器内的所有元素对的键(KEY)都是唯一不可重复的。

**存储原理:**根据键的hashCode 值存储于哈希表中。

  • JDK 1.8之前:哈希表 = 数组 + 链表 + (哈希算法)
  • JDK 1.8及之后:哈希表 = 数组 + 链表 + 红黑树(据哈希值排序) + (哈希算法)

HashMap中的Key如何保证不重复?

  • 对于基本数据类型,直接判断值大小去重复,并不需要开发者过多干预;
  • 对于引用类型,通过比较哈希值(hashCode)进行比较(equals),所以需要重写hashCode()和equals方法;

保证键唯一不可重复的原理:

  1. 往HashMap添加元素的时候,首先会调用hashCode方法获得键(KEY)的哈希码值;
  2. 根据该哈希码就可以算出该元素在哈希表中的存储位置。
  3. 如果算出的位置目前没有任何元素存储,那么该元素可以直接添加到哈希表中。
  4. 如果算出的位置目前已经存在其他的元素,那么还会调用该元素的equals方法与这个位置上的元素进行比较,如果equals方法返回的是false,那么该元素允许被存储;如果equals方法返回的是true,那么该元素被视为重复元素,不允存储。
//第一个HashMap实例
public class Main {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("aa","AAA");
        map.put("bb","BBB");
        map.put("cc","CCC");
        map.put("bb","BbbbB"); //如果出现了相同键,则覆盖。

        for(String key:map.keySet()) { //增强for
            System.out.println("Key:"+key+"  Value:"+map.get(key));
        }
        //Key:aa  Value:AAA
        //Key:bb  Value:BbbbB
        //Key:cc  Value:CCC
    }
}

注:LinkedHashMap类保证迭代的顺序。保存了记录的插入顺序,在用Iterator 遍历LinkedHashMap
时,先得到的记录肯定是先插入的


TreeMap类

TreeMap容器内的所有元素对的键(KEY)都是有序的。

TreeMap特性:

  • 按照键默认升序排序
  • 排序的底层实现是基于红黑树(二叉树)

Key的排序规则(与TreeSet一样):

  • Key是基本数据类型:直接按大小,进行升序排序;进行排序的底层实现是:红-黑树;
  • Key是字符串类型:按照字典顺序(即ASCII码表)逐个排序;进行排序的底层实现是:红-黑树;
  • Key是自定义类:TreeSet无法进行默认排序,需要定义排序规则;

Key为自定义类的自动排序规则(与TreeSet一样):

  • 方案一:直接在类上实现Comparable接口,重写比较方法;优先级较高。
  • 方案二:写一个专门的比较器类(实现Comparator接口的类,并重写比较方法),需要比较时将比较器传入;

TreeMap架构:
【Java集合】Map接口,及其实现类HashMap、TreeMap详解_第3张图片

具备自然顺序

import java.util.TreeMap;

public class Main {
    public static void main(String[] args) {
        TreeMap<Character, Integer> tree = new TreeMap<Character, Integer>();
        tree.put('c',10);
        tree.put('b',2);
        tree.put('a',5);
        tree.put('h',12);
        System.out.println(tree); //键的会自动根据字典顺序,排好序输出
    }
}
//输出:{a=5, b=2, c=10, h=12}

不具备自然顺序

实现Comparable接口、定义CompareTo方法:

import java.util.Comparator;
import java.util.TreeMap;
class Emp implements Comparable<Emp>{
	
	String name;
	
	int salary;

	public Emp(String name, int salary) {
		super();
		this.name = name;
		this.salary = salary;
	}
	
	@Override
	public String toString() {
		return "[姓名:"+this.name+" 薪水:"+ this.salary+"]";
	}

	@Override
	public int compareTo(Emp o) {
		return this.salary - o.salary;
	}
	
}
public class Demo6 {
	
	public static void main(String[] args) {

		TreeMap<Emp, String> tree = new TreeMap<Emp, String>(comparator);
		tree.put(new Emp("冰冰", 2000),"001");
		tree.put(new Emp("家宝", 1000),"002");
		tree.put(new Emp("习总", 3000),"003");
		tree.put(new Emp("克强", 5000),"005");
		
		System.out.println(tree);
	}
}

传入比较器

创建TreeMap对象时传入比较器:

class Emp {
	String name;
	
	int salary;

	public Emp(String name, int salary) {
		super();
		this.name = name;
		this.salary = salary;
	}

	@Override
	public String toString() {
		return "[姓名:"+this.name+" 薪水:"+ this.salary+"]";
	}
	
}

//自定义一个比较器
class MyComparator implements Comparator<Emp>{

	@Override
	public int compare(Emp o1, Emp o2) {
		return o1.salary - o2.salary;
	}
}

public class Demo6 {
	
	public static void main(String[] args) {
		//创建一个自定义比较器
		MyComparator comparator = new MyComparator();
		
		TreeMap<Emp, String> tree = new TreeMap<Emp, String>(comparator);
		tree.put(new Emp("冰冰", 2000),"001");
		tree.put(new Emp("家宝", 1000),"002");
		tree.put(new Emp("习总", 3000),"003");
		tree.put(new Emp("克强", 5000),"005");
		
		tree.put(new Emp("财厚", 5000),"008");
		System.out.println(tree);
	}
}

你可能感兴趣的:(#,JavaSE,Map接口,HashMap,TreeMap,LinkedHashMap)