欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-02-06 23:51:16
...

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

集合体系

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

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

Map接口

**定义:**Map是一种依照键(key)存储元素的容器,键(key)很像下标,在List中下标是整数。在Map中键(key)可以使任意类型的对象。

特点:

  • 存储的数据都是以键值对的形式存在的,键不可重复,值可以重复。

Collection接口出发理解Map接口

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

  • Collection中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方式存储
  • Map中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的值。
  • Collection中的集合称为单列集合,Map中的集合称为双列集合。
  • 需要注意的是,Map中的集合不能包含重复的键,值可以重复;每个键只能对应一个值。
  • Map中常用的集合为HashMap集合、LinkedHashMap集合。

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

方法

添加:

  1. V put(K key, V value) // 如果之前没有存在该键,那么返回的是null,如果之前就已经存在该键了,那么就返回该键之前对应的值。
  2. void putAll(Map<? extends K,? extends V> 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<K> keySet() : 以Set的形式获返回所有的key
		Set<String> keys = map.keySet();
		for (String key : keys) {
			System.out.println(key);
		}
		System.out.println("-----------");
		
		//Collection<V> values() :以Collection形式返回所有Value
		Collection<String> values = map.values();
		for (String value : values) {
			System.out.println(value);
		}
	}
}

迭代

三种迭代方式:

  1. keySet()方法:
    a) 调用map的方法:keySet,将所有的键存储到Set集合中
    b) 遍历该Set集合,获取出Set集合中的所有元素(即为Map中的键)
    c) 调用Map集合的方法:get,通过键获取到对应的值
  2. values()
  3. entrySet()
    a) 调用map的方法:entrySet,将集合中的映射关系对象存储到Set集合中
    b) 迭代Set集合,获取出Set集合的元素,是映射关键对象
    c) 通过映射关系对象方法getKey,getValue获取键值对
//迭代方法的使用
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class Main {
	public static void main(String[] args) {
		Map<String, String> map = new HashMap<String, String>();
		// 添加方法
		map.put("汪峰", "章子怡");
		map.put("文章", "马伊琍");
		map.put("谢霆锋", "张柏芝");
		map.put("成龙", "林凤娇");

		// map集合中遍历方式一: 使用keySet方法进行遍历
		// 缺点: keySet方法只是返回了所有的键,没有值。
		Set<String> keys = map.keySet();
		// a. 把Map集合中的所有键都保存到一个Set类型(因为key不能重复) 的集合对象中返回。
		// b. 迭代器实现(也可以使用for循环)
		Iterator<String> it01 = keys.iterator(); // 返回一个抓取keys的爪子,名为“it";
		while (it01.hasNext()) {
			String key = it01.next();
			System.out.println("键:" + key + " 值:" + map.get(key)); //c.
			// System.out.println("键:"+it.next()+" 值:"+map.get(it.next()));不能这样使用:next跑动了两次;
		}

		// map集合的遍历方式二: 使用values方法进行遍历。
		// 缺点: values方法只能返回所 的值,没有键。
		Collection<String> c = map.values(); // 把所有的值存储到一个Collection集合(因为value可以重复)中返回。
		Iterator<String> it02 = c.iterator();
		while (it02.hasNext()) {
			System.out.println("值:" + it02.next());
		}

		// map集合的遍历方式三: entrySet方法遍历。返回键和值
		Set<Map.Entry<String, String>> entrys = map.entrySet(); // Entry的本质是一个同时含有键和值的类方法(静态,详见源代码);
		Iterator<Map.Entry<String, String>> it03 = entrys.iterator();
		while (it03.hasNext()) {
			Map.Entry<String, String> entry = it03.next();
			System.out.println("键:" + entry.getKey() + " 值:" + entry.getValue());
		}

		// 增强for循环
		for (Map。Entry<Integer, String> entry : map.entrySet()) {
			System.out.println("key:" + entry.getKey + ";value:" + entry.getValue);
		}
	}
}

HashMap类

存储原理:
往HashMap添加元素的时候,首先会调用键的hashCode方法得到元素 的哈希码值,然后经过运算就可以算出该元素在哈希表中的存储位置。

  • 情况1:如果算出的位置目前没有任何元素存储,那么该元素可以直接添加到哈希表中。
  • 情况2:如果算出的位置目前已经存在其他的元素,那么还会调用该元素的equals方法与这个位置上的元素进行比较,如果equals方法返回的是false,那么该元素允许被存储,如果equals方法返回的是true,那么该元素被视为重复元素,不允存储
class Person{
	
	int id;
	
	String name;

	public Person(int id, String name) {
		super();
		this.id = id;
		this.name = name;
	}
	
	@Override
	public String toString() {
		return  "[编号:"+this.id+" 姓名:"+ this.name+"]";
		
	}	
	
	@Override
	public int hashCode() {
		return this.id;
	}
	
	@Override
	public boolean equals(Object obj) {
		Person p = (Person) obj;
		return this.id== p.id;
	}
}

public class Demo5 {
	
	public static void main(String[] args) {
		HashMap<Person, String> map = new HashMap<Person, String>();
		map.put(new Person(110,"狗娃"), "001");
		map.put(new Person(220,"狗剩"), "002");
		map.put(new Person(330,"铁蛋"), "003");
		map.put(new Person(110,"狗娃"), "007");  //如果出现了相同键,那么后添加的数据的值会取代之前 的值。
		System.out.println("集合的元素:"+ map);
	}
}

LinkedHashMap类

功能:

  1. 保证迭代的顺序

特性:

  1. 继承自HashMap

TreeMap类

**工作原理:**TreeMap也是基于红黑树(二叉树)数据结构实现 的, 特点:会对元素的键进行排序存储。

TreeMap 核心特性:

  • 往TreeMap添加元素的时候,如果元素的键具备自然顺序,那么就会按照键的自然顺序特性进行排序存储。
  • 往TreeMap添加元素的时候,如果元素的键不具备自然顺序特性, 那么键所属的类必须要实现Comparable接口,把键的比较规则定义在CompareTo方法上。
  • 往TreeMap添加元素的时候,如果元素的键不具备自然顺序特性,而且键所属的类也没有实现Comparable接口,那么就必须在创建TreeMap对象的时候传入比较器

具备自然顺序

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); //键的会自动根据字典顺序,排好序输出

不具备自然顺序

实现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);
	}
}

Hashtable类(已淘汰)

底层是依赖了哈希表实现,线程安全,操作效率低;
从JDK1.2,被更先进HashMap替代

Collections类

Collections类特性:

  1. 集合框架中的工具类,方法都是用于操作Collection;例如Arrays是数组的工具类;
  2. 类中的所有方法都是静态的,不可创建新对象;
  3. 具体方法,见API;