在我的博客: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集合的具体介绍!