JavaSE之集合类的Map集合接口

在我的博客:JavaSE之集合类的Collection集合接口 中介绍了集合类的Collection集合接口,本文章主要介绍集合类的Map集合接口。

Collection集合的特点是每次进行单个对象的保存,如果现在需要对一对对象(偶对象)进行保存,就需要使用Map集合接口来完成。即Map集合中一次保存两个对象,并且这两个对象的关系结构为:key=value结构,这种结构的最大特点是可以通过key值找到value值。

1. Map接口

首先Map接口的定义:

public interface Map<K,V>

在Map接口中常用方法有如下几个:

(1)向Map集合中添加数据:

    V put(K key, V value);

(2)根据key值取得对应的value值(若没有返回null):

    V get(Object key);

(3)取得所有key值对象保存在Set集合中(key值不能重复):

    Set<K> keySet();

(4)取得所有value值对象保存在Collection集合中(value值可以重复):

    Collection<V> values();

(5)将Map集合变成Set集合:

    Set<Map.Entry<K, V>> entrySet();

由于Map本身是接口,要使用Map需要通过其子类实例化对象,Map接口的常用子类有四个:HashMap、Hashtabe、TreeMap、ConcurrentHashMap。下面一一介绍。

2. 实现Map接口的子类------HashMap类

HashMap是使用Map集合中最为常用的子类。下面对Map集合进行基本的操作:

package www.bit.java.work;

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class Test5 {
	public static void main(String[] args) {
		Map<Integer,String> map=new HashMap<>();
		System.out.println("添加数据:");
		//1.添加数据
		map.put(1,"数据1");
		map.put(1,"数据2");
		map.put(2,"数据2");
		map.put(3,"数据3");
		//输出集合数据
		System.out.println(map);
		System.out.println("根据key值取得value值:");
		//2.根据key值取得value
		System.out.println(map.get(1));
		System.out.println("取得Map集合中的所有key值:");
		//3.取得Map集合中的所有key值
		Set<Integer> set=map.keySet();
		//输出Set集合
		Iterator<Integer> iterator=set.iterator();
		while(iterator.hasNext()) {
			System.out.println(iterator.next());
		}
		System.out.println("取得Map集合中的所有value值:");
		//4.取得Map集合中的所有value值
		Collection<String> collection=map.values();
		//输出Collection集合
		Iterator<String> iterator2=collection.iterator();
		while(iterator2.hasNext()) {
			System.out.println(iterator2.next());
		}
		System.out.println("将Map集合变为Set集合:");
		//5.将Map集合变为Set集合
		Set<Map.Entry<Integer,String>> set2=map.entrySet();
		Iterator<Map.Entry<Integer,String>> iterator3=set2.iterator();
		while(iterator3.hasNext()) {
			System.out.println(iterator3.next());
		}
	}
}

运行结果如下:

添加数据:
{1=数据2, 2=数据2, 3=数据3}
根据key值取得value值:
数据2
取得Map集合中的所有key值:
1
2
3
取得Map集合中的所有value值:
数据2
数据2
数据3
将Map集合变为Set集合:
1=数据2
2=数据2
3=数据3

通过代码以及运行结果可知,在Map集合中不会出现重复的key值,若添加了多个key值相等的数据则后一个数据覆盖前一个数据。总结Map集合:

(1)key值不允许重复,如果重复则会将对应的value值更新。

(2)key值和value值均允许为null。

解释HashMap原理:(重点)

在数据量小(JDK1.8后阈值为8前)时,HashMap是按照数组+链表的模式存储的;当数据量大(JDK1.8后阈值为8)时,为了快速查找将存储形式变为红黑树(均衡二叉树)来进行数据存储,用hash码作为数据定位。

3. 实现Map接口的子类------Hashtable类

在JDK1.0提供了三大主要类:Vector、Enumeration、Hashtable,Hashtable类是最早实现这种二院偶对象数据结构的,Hashtable类不允许其key值、value值为null。下面对Map集合进行基本操作:

package www.bit.java.work;

import java.util.Hashtable;
import java.util.Map;

public class Test5 {
	public static void main(String[] args) {
		Map<Integer, String> map = new Hashtable<>();
		System.out.println("添加数据:");
		// 1.添加数据
		map.put(1, "数据0");
		map.put(1, "数据1");
		map.put(2, "数据2");
		map.put(3, "数据3");
		System.out.println(map);
	}
}

运行结果:

添加数据:
{3=数据3, 2=数据2, 1=数据1}

总结HashMap与Hashtable的区别:

(1)版本方面:HashMap在JDK1.2提出;Hashtable在JDK1.0提出。

(2)性能方面:HashMap异步处理,性能高;Hashtable同步处理,性能低。

(3)安全方面:HashMap线程不安全;Hashtable线程安全。

(4)添加操作:HashMap允许key值、value值为null;Hashtable均不允许key值、value值为null。

4. 实现Map接口的子类-------ConcurrentHashMap类

ConcurrentHashMap的特点=HashMap的高性能+Hashtable的线程安全。在使用ConcurrentHashMap时,既可以保证多个线程数据更新时的同步,又可以保证高效的查询速度。但需要注意:ConcurrentHashMap不允许key值以及value值为null。下面对Map集合进行基本操作:

package www.bit.java.work;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class Test5 {
	public static void main(String[] args) {
		Map<Integer, String> map = new ConcurrentHashMap<>();
		System.out.println("添加数据:");
		// 1.添加数据
		map.put(1, "数据0");
		map.put(1, "数据1");
		map.put(2, "数据2");
		map.put(3, "数据3");
		System.out.println(map);
	}
}

运行结果:

添加数据:
{1=数据1, 2=数据2, 3=数据3}

解释ConcurrentHashMap的工作原理(重点):

ConcurrentHashMap将保存的大量数据平均分在不同的桶(数据区域)中,这样在进行数据查找时就可以避免全局的数据扫描;采用了分桶之后每一个数据中必须有一个明确的分桶标记,我们一般采用hash码(hashCode()方法)。ConcurrentHashMap实现高性能的原因是:①ConcurrentHashMap是在数据的某一个区域(桶)上上锁,故数据更新时只锁某一个数据区域(桶),而其他不受影响;②在锁的区域使用读写锁,读异步而写同步,故即使在同一个桶中,数据读取依旧不受影响。

5. 实现Map接口的子类------TreeMap类

TreeMap类是一个可以排序的Map接口的子类,它是按照key值的内容进行排序。下面对Map集合进行基本操作:

package www.bit.java.work;

import java.util.Map;
import java.util.TreeMap;

public class Test5 {
	public static void main(String[] args) {
		Map<Integer, String> map = new TreeMap<>();
		map.put(10, "数据10");
		map.put(4, "数据4");
		map.put(2, "数据2");
		map.put(13, "数据13");
		System.out.println(map);
	}
}

运行结果:

{2=数据2, 4=数据4, 10=数据10, 13=数据13}

分析TreeMap排序的原因:

既然TreeMap子类可以进行排序,那么我们可以利用TreeMap实现数据的排序处理操作。要想进行集合的数据排序实际是根据添加的偶对象进行排序,而如果要其偶对象进行排序就需要其对象所在类必须实现Comparable接口并覆写compareTo()方法,只有通过此方法才能比较大小关系。而我们上面的代码中的对象所在类String类、Integer类(基本数据类型)均实现了Comparable接口并覆写了compareTo()方法,故可以使用TreeMap类对集合数据进行排序。

下面观察将简单Java类作为key值,系统类作为value值的Map集合数据时,简单Java类实现Comparable接口并覆写compareTo()方法从而实现集合数据顺序存储的操作:

package www.bit.java.work;

import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;

class Person implements Comparable<Person>{
	private String name;
	private int age;
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = 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;
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
	@Override
	public int compareTo(Person person) {
		if(this.age>person.age) {
			return 1;
		}else if(this.age<person.age) {
			return -1;
		}else {
			return this.name.compareTo(person.name);
		}
	}
}
public class Test5 {
	public static void main(String[] args) {
		Map<Person,String> map=new TreeMap<>();
		map.put(new Person("zhang",20),"ABC");
		map.put(new Person("zhang",19),"ABC");
		map.put(new Person("li",20),"ABC");
		System.out.println(map);
	}
}

运行结果如下:

{Person [name=zhang, age=19]=ABC, Person [name=li, age=20]=ABC, Person [name=zhang, age=20]=ABC}

总结TreeSet类:在使用TreeSet类实例化Map接口对象时,其偶对象所在类必须实现Comparable接口并覆写compareTo()方法,否则就会出现 java.lang.ClassCastException异常。

关于Map集合中key值的说明:

由于Map集合中key值一旦重复则将其对应的value值进行更新,故在Map集合中key值是不重复唯一的,那么是什么保证了key值不重复唯一性的呢?是Object类的hashCode()方法和equals()方法保证了key值的不重复唯一性。在之前的操作中,我们使用的是系统类中的基本数据类型作为key值,而系统类已经覆写了Object类的hashCode()方法以及equals()方法故能够保证key值的不重复唯一性。其实也可以将用户自定义类对象作为key值,此时就需要该自定义类覆写Object类的hashCode()方法和equals()方法。

下面将用户自定义类对象作为key值,系统类String类作为value值进行Map集合的基本操作:

package www.bit.java.work;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

class Person{
	private String name;
	private Integer age;
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = 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;
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
	@Override
	public int hashCode() {
		return Objects.hash(name,age);
	}
	@Override
	public boolean equals(Object obj) {
		if(obj==this) {
			return true;
		}
		if(obj==null||obj.getClass()!=this.getClass()) {
			return false;
		}
		Person person=(Person)obj;
		return this.name.equals(person.name)&&this.age.equals(person.age);
	}
}
public class Test5 {
	public static void main(String[] args) {
		Map<Person,String> map=new HashMap<>();
		map.put(new Person("zhang",20),"ABC");
		//key值重复
		map.put(new Person("zhang",20),"DEF");
		map.put(new Person("li",20),"HIG");
		System.out.println(map);
	}
}

运行结果如下:

{Person [name=li, age=20]=HIG, Person [name=zhang, age=20]=DEF}

若Perosn类没有覆写Object类的hashCode()方法以及equals()方法的话,运行结果将如下所示:

{Person [name=zhang, age=20]=DEF, Person [name=li, age=20]=HIG, Person [name=zhang, age=20]=ABC}

故,在今后将用户自定义类对象作为Map集合的key值时,需要用户自定义类覆写Object类的hashCode()方法以及equals()方法才能保证key值的唯一性。

以上就是对Map集合的具体介绍!


猜你喜欢

转载自blog.csdn.net/tongxuexie/article/details/80471515