1 Map的通性
一种键值对类型的集合,每次向集合添加一对元素,添加“主键Key=值Value”。其中只能在新建时传入且唯一,传入后不得修改;值不唯一,可直接修改。
1.1 基本用法与特点
-
创建Map对象
HashMap<主键类型,值类型> map = new HashMap<>(); TreeMap<主键类型,值类型> map = new TreeMap<>();
-
添加元素
map.put(主键,值); map1.putAll(map2);
-
得到集合的大小
map.size();
-
根据主键得到值对象
map.get(主键); -> 值
-
判断集合里是否出现指定的主键对象
map.containsKey(主键);
-
判断集合里是否出现指定的值对象
map.containsValue(值);
-
根据主键删除元素
map.remove(主键);
-
若想快速判断Map中某个键/值是否存在,要善用contains()方法。根据类中定义好的equals()方法,查看是否存在,可以直接返回boolean值。同样使用remove(主键)时,也可以通过equals()方法定制删除主键的规则。
1.2 遍历
姓名 | 得分 |
---|---|
- 通过Map集合得到所有的主键对象(主键对象是唯一且无序的,故使用Set来接收即可)能获取到的是:
姓名 |
---|
Set<主键> set = map.keySet();
得到主键的同时,主键是唯一的,可以根据主键得到值对象:map.get(主键),只能根据主键得到对应的值对象而不能改。
- 通过Map集合得到所有的值对象 (主键对象是不唯一且无序的,故只能使用Collection来接收),能获取到的是:
得分 |
---|
Collection<值> coll = map.values();
无法根据值得到对应的主键,因为值是不唯一的,只能将值取出。
-
通过Map集合得到所有记录的主键和值且可以对值进行修改
Set<Map.Entry<主键,值>> set = map.entrySet(); 对set遍历可以获取到每一条记录的主键和值 通过记录得到主键对象:记录.getKey() 通过记录得到值对象:记录.getValue() 通过记录修改值对象:记录.setValue(XXX)
能获取到的是一条一条的:
姓名 | 得分 |
---|---|
//将Eric的成绩 + 20分
import java.util.*;
public class Test{
public static void main(String[] args){
HashMap<String,Integer> map = new HashMap<>();
//添加元素:
map.put("Allen",455);
map.put("Eric",122);
map.put("Cindy",600);
//通过Map集合得到一条记录 -> [主键,值]
Set<Map.Entry<String,Integer>> set = map.entrySet();
//通过对集合每一条记录遍历,得到x -> 即集合里面的每一条记录
for(Map.Entry<String,Integer> x : set){
//通过记录得到主键对象
String name = x.getKey();
//通过记录得到值对象
Integer score = x.getValue();
if(name.equals("Eric")){
x.setValue(score + 20);
}
}
System.out.println(set);//--->[Cindy=122, Allen=455, Eric=620]
System.out.println(map);//--->[Cindy=122, Allen=455, Eric=620]
}
}
-
因Map是一个键值对,当单独取值key或value时,不要对其进行修改,只作为一个遍历查找的简便方法。对单独取值得到的集合进行add()操作会报UnsupportOperationException。但可以对Set类型的主键集合中元素进行删除,进而Map中整个元素被删除,因为其具有唯一性。
-
主键不能直接修改,若修改需要先remove(主键)之前的,再put(主键,值)进新的键值对。
-
值可以修改,使用setValue() 或 直接使用put(旧主键,新值)进行覆盖。
-
在使用迭代器或foreach进行遍历时注意CME异常,要使用迭代器的remove方法,增加新元素时需要使用新集合接收,在循环外将新集合加入旧集合。
2 HashMap集合的特性
- Map接口的实现类,方法与Map的通用方法相同,但底层需遵循之前学过的hashCode()、==、equals()的比较机制。
- HashMap的特性与HashSet的其他特性也类似,比如在对集合中元素修改时,不能对已传入的元素的与哈希特征值生成有关的属性进行直接修改。
import java.util.*;
public class Test{
public static void main(String[] args){
HashMap<Student,Integer> map = new HashMap<>();
Student s1 = new Student("张三", 18);
Student s2 = new Student("李四", 22);
Student s3 = new Student("王五", 26);
Student s4 = new Student("zz", 25);
map.put(s1,77);
map.put(s2,88);
System.out.println(map.containsKey(s4));//--->true
//因Student对象的hashCode()和equals()都一样
//张三、李四被视为相等对象,新加入的李四对象不会加入集合,但年龄所对应的Integer类hashCode()值有不同,故可以加入集合
//同理可以考虑主键名不变,修改value值可以put(旧主键名, 新值)直接覆盖
//传入的主键相同,但主键是唯一的,所以不再添加新的,但value值不同,故可以添加
System.out.println(map);//--->{张三=88}
map.remove(s3);
System.out.println(map);//--->{}
}
}
class Student{
String name;
int age;
public Student(String name,int age){
this.name = name;
this.age = age;
}
@Override
public String toString(){
return name;
}
@Override
public boolean equals(Object obj){
return true;//任何两个对象都视为相等对象
}
@Override
public int hashCode(){
return 1;//任何对象的哈希码值都是1
}
}
-
HashMap和Hashtable之间的区别(Hasntable为最早的Map集合形式)
-
同步特性
Hashtable同一时时间允许一个线程进行访问,效率较低,但是不会出现并发错误
HashMap同一时间允许多个线程进行访问,效率较高,但是可能会出现并发错误JDK5.0开始集合的工具类(Collections)里面提供一个方法(synchroniedMap()) 可以将线程不安全的HashMap变成线程安全的集合对象,所以Hashtable被淘汰。但添加了synchronizedMap()之后的HashMap与HashTable无异,都是在分组前上锁来控制传入的数据,这时只能逐个进入分组;
JDK5.0后不如使用util包下concurrent子包中的ConcurrentHashMap集合,对每个小组加锁,允许进入多个数据,效率更高。 -
对null容忍度不同
HashMap无论是主键还是值对象,都可以存放null。由于主键是唯一,所以主键只能存放一个null,Hashtable无论是主键还是值对象,都不能存放null,否则都会空指针异常。 -
底层分组不同
HashMap默认分16个小组,程序员可以按照自己的意愿随意的进行指定分组组数,但是最终一定是2的n次方数,比如定义为17组,最后系统会自动开辟32组。
Hashtable默认分11个小组,程序员可以按照自己的意愿随意指定分组组数。 -
出现的版本不同
Hashtable适用于JDK1.0及以后,
HashMap适用于JDK1.2及以后。
-
3 TreeMap集合的特性
- SortedMap接口的实现类,方法与Map的通用方法相同,但底层需遵循之前学过的compareTo()/compare的比较机制。
- TreeMap的特性与TreeSet的其他特性也类似,比如在对集合中元素修改时,不能对已传入的元素的与compareTo()/compare有关的属性进行直接修改。
import java.util.*;
public class Test{
public static void main(String[] args){
TreeMap<Student,Integer> map = new TreeMap<>();
Student s1 = new Student("张三",18);
map.put(s1,77);
//compareTo()一直向右比较,因返回值无法返回0而不会停止比较,所以remove()方法无法匹配并返回值
map.remove(s1);
System.out.println(map);//--->{张三=77}
//compareTo()一直向右比较,因返回值无法返回0而不会停止比较,所以containsKey()方法无法匹配并返回值
System.out.println(map.containsKey(s1));//--->false
//compareTo()一直向右比较,因返回值无法返回0而不会停止比较,所以get()方法无法匹配并返回值
System.out.println(map.get(s1));//--->null
System.out.println(map.size());//--->1
map.put(s1,77);
//因compareTo()返回值为1可以无限向右子树添加相同的元素
System.out.println(map.size());//--->2
}
}
class Student implements Comparable<Student>{
String name;
int age;
public Student(String name,int age){
this.name = name;
this.age = age;
}
@Override
public String toString(){
return name;
}
@Override
public int compareTo(Student stu){
return 1;
}
}