1、Map接口详解
(1)映射(map)是一个存储键/值对的对象。给定一个键,可以查询到它的值,键和值都是对象。
(2)键必须是唯一的,值可以重复。
(3)有些映射可以接收null键和null值,而有的不能。
(4)Map接口定义的方法:int size();boolean isEmpty();boolean containsKey(Object key);boolean containsValue(Object value);V get(Object key);V put(K key,V value);V remove(Object key);Collection
(5)Map.Entry接口代表映射项(键-值对)类型,是Map的嵌套类型(是Map的内部类)。
(6)Map.Entry接口定义的方法:K getKey();V getValue();V setValue(V value)。
2、HashMap及常用API
(1)HashMap类是基于哈希表的map接口的实现,并允许使用null键和null值。
(2)构造方法:HashMap();HashMap(Map m);HashMap(int capacity);HashMap(int capacity,float fillRatio)。
(3)HashMap实现并扩展AbstractMap,本身并没有增加任何新的方法。
(4)散列映射不保证它的元素的顺序,元素加入散列映射的顺序并不一定是它们被迭代读出的顺序。
(5)HashMap常用方法举例
package com.iotech.map;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;
public class HashMapDemo1 {
public static void main(String[] args) {
//HashMap<String,String> hashmap=new HashMap<String, String>();//构造一个空的 HashMap ,默认初始容量(16)和默认负载系数(0.75)。
HashMap<String,String> hashmap=new HashMap<String, String>(1);//构造一个空的 HashMap具有指定的初始容量和默认负载因子(0.75)。
hashmap.put("jay", "张三");
hashmap.put("jay", "李四");
hashmap.put("rose", "玫瑰");
hashmap.put("Mary", "小红");
System.out.println(hashmap);
//获取map中的所有键
/*Set<String> keysSet=hashmap.keySet();
for(String name:keysSet){
System.out.println(name);
}*/
//获取map中的所有值
Collection<String> values=hashmap.values();
for(String value:values){
System.out.println(value);
}
//得到key的同时得到key所对应的值
Set<String> keysSet=hashmap.keySet();
for(String name:keysSet){
System.out.println(name+"-->"+hashmap.get(name));
}
System.out.println(hashmap.size());
System.out.println(hashmap.isEmpty());
//当我们调用put(key,value)方法的时候,首先会把key和value封装到Entry这个静态内部类对象中。
//把Entry对象再添加到数组中,所以我们想获取map中的所有键值对,我们只要获取数组中的所有Entry对象,
//接下来调用Entry对象中的getkey()和getvalue()方法就能获取键值对。
Set<Entry<String,String>> entrySet=hashmap.entrySet();
for(Entry<String, String> entry:entrySet){
System.out.println(entry.getKey()+"-->"+entry.getValue());
}
/*
* hashmap调用默认构造方法会产生一个底层长度为16的Entry数组
* int hash=hash(key.hashCode());
* 首先调用key的hashCode方法来得到一个整数(哈希码),把哈希码作为参数传到hash函数中来进行运算(散列运算)
* 得到一个整型(散列值)。hashCode()方法能够提高哈希表的性能。
* int i=indexFor(hash,table.length);
* 把散列值和数组的长度来进行运算,最终得到entry对象存放到数组的位置(下标)。
*
* hashmap内部的结构是一个数组链表结构。因为不同的key有可能算出来是相同的散列值,根据散列值计算出存放数组
* 的下标会冲突。
*/
}
}
3、哈希码的产生和使用
(1)hashCode的常规协定:在Java应用程序执行期间,在对同一对象多次调用hashCode方法时,必须一致地返回相同的整数,前提是将对象进行equals比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
(2)如果根据equals(Object)方法,两个对象是相等的那么这两个对象中的每个对象调用hashCode方法都必须生成相同的整数结果。(这里的equals方法是指Object类中没有被子类重写过的equals方法)。
(3)如果根据equals(java.lang.Object)方法,两个对象不相等,那么对这两个对象中的任一对象上调用hashCode方法不要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。
(4)举例
package com.iotech.map;
import java.util.HashMap;
import java.util.Map;
public class HashCodeDemo2 {
public static void main(String[] args) {
Map<Student, String> map=new HashMap<Student, String>();
map.put(new Student("jay", 20), "张三");
map.put(new Student("lisi", 30), "李四");
map.put(new Student("rose", 20), "玫瑰");
map.put(new Student("lisi", 30), "陈豪");
/*要满足student键值相等,首先需要hashCode相等,另外还需要key的equals方法相等。所以需要重写
hashCode()和equals()方法。*/
System.out.println(map);
System.out.println(map.size());
}
}
class Student{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
4、TreeMap及常用API
(1)TreeMap类通过使用红黑树实现Map接口。
(2)TreeMap提供按排序顺序存储键/值对的有效手段,同时允许快速检索。
(3)TreeMap不像散列映射,树映射保证它的元素按关键字升序排序。
(4)TreeMap的构造方法:TreeMap();TreeMap(Comparator comp);TreeMap(Map m);TreeMap(SortedMap sm)。
(5)TreeMap实现SortedMap并且扩展AbstractMap,它本身没有定义其他的方法。
(6)举例一
package com.iotech.map;
import java.util.Set;
import java.util.TreeMap;
import java.util.Map.Entry;
public class TreeMapDemo1 {
public static void main(String[] args) {
TreeMap<String,String> treeMap=new TreeMap<String, String>();
treeMap.put("jack", "zhangsan");
treeMap.put("mary", "xiaohong");
treeMap.put("rose", "xiaozhang");
treeMap.put("free", "xiaoming");
treeMap.put("rose", "chenhao");
//TreeMap是按照键来进行排序的,而String实现了comparable接口,所以是没有问题的。
System.out.println(treeMap);
Set<Entry<String, String>> entrySet=treeMap.entrySet();
for(Entry<String, String> entry:entrySet){
System.out.println(entry.getKey()+"-->"+entry.getValue());
}
}
}
(7)举例二
package com.iotech.map;
import java.util.Comparator;
import java.util.TreeMap;
public class TreeMapDemo2 {
public static void main(String[] args) {
TreeMap<Person,String> treeMap=new TreeMap<Person,String>(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
int x=o1.getAge()-o2.getAge();
if(x>0) return 1;
else if(x<0) return -1;
return 0;
}
});
treeMap.put(new Person("zhangsan", 30), "张三");
treeMap.put(new Person("lisi", 31), "李四");
treeMap.put(new Person("rose", 32), "玫瑰");
treeMap.put(new Person("zhangsan", 33), "张三");
//这里的Person作为TreeMap中的键,也是需要进行排序的,所以Person类也需要继承Comparable接口,
//因此也需要重写CompareTo()方法。
System.out.println(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 int compareTo(Person o) {
int x=this.age-o.getAge();
if(x>0){
return 1;
}else if(x<0){
return -1;
}
return 0;
}*/
}
5、Comparator和Comparable接口
TreeMap的key存储引用数据类型,需要满足一定条件,要么引用类型实现Comparable接口,要么为该TreeMap容器提供实现Comparator接口的比较器对象。对应上面例二的两种实现方式。
6、案例讲解
(1)给定一个字符数组,数组中内容有重复,现要求打印输出各个字符串出现的次数。
(2)使用HashMap来设计,HashMap的key可以用来保存字符串,value用来保存字符串所对应的次数,从HashMap中获取不存在的key所对应的值为null。
(3)代码实现
package com.iotech.map;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;
public class HashMapTest {
public static void main(String[] args) {
String[] string={"zhangsan","lisi","wangwu","jack","zhangsan","zhangsan","wangwu"};
HashMap<String,Integer> hashMap=new HashMap<String,Integer>();
for(int i=0;i<string.length;i++){
if(null==hashMap.get(string[i])){
hashMap.put(string[i], 1);
}else{
hashMap.put(string[i], hashMap.get(string[i])+1);
}
}
Set<Entry<String,Integer>> entrySet=hashMap.entrySet();
for(Entry<String,Integer> entry:entrySet){
System.out.println(entry.getKey()+"-->"+entry.getValue());
}
}
}