文章目录
- 简述
- Map中key-value特点
- Map接口的常用方法
- Map的主要实现类:HashMap
- HashMap概述
- Map实现类之二:LinkedHashMap
- Map实现类之三:TreeMap
- Map实现类之四:Hashtable(古老实现类)
- Map实现类之五:Properties(古老的类)
简述
-
Map与Collection并列存在。用于保存具有
映射关系
的数据:key-valueCollection
集合称为单列集合,元素是孤立存在的(理解为单身)。Map
集合称为双列集合,元素是成对存在的(理解为夫妻)。
-
Map 中的 key 和 value 都可以是任何引用类型的数据。但常用String类作为Map的“键”。
-
Map接口的常用实现类:
HashMap
、LinkedHashMap
、TreeMap
和``Properties。其中,HashMap是 Map 接口使用
频率最高`的实现类。
Map中key-value特点
这里主要以HashMap为例说明。HashMap中存储的key、value的特点如下:
Map 中的 key用Set来存放
,不允许重复
,即同一个 Map 对象所对应的类,须重写hashCode()和equals()方法
-
key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到唯一的、确定的 value,不同key对应的
value可以重复
。value所在的类要重写equals()方法。 -
key和value构成一个entry。所有的entry彼此之间是
无序的
、不可重复的
。
Map接口的常用方法
- 添加、修改操作:
- Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
- void putAll(Map m):将m中的所有key-value对存放到当前map中
- 删除操作:
Object remove(Object key)
:移除指定key的key-value对,并返回valuevoid clear()
:清空当前map中的所有数据
- 元素查询的操作:
Object get(Object key)
:获取指定key对应的valueboolean containsKey(Object key)
:是否包含指定的keyboolean containsValue(Object value)
:是否包含指定的valueint size()
:返回map中key-value对的个数
-boolean isEmpty()
:判断当前map是否为空boolean equals(Object obj)
:判断当前map和参数对象obj是否相等
- 元视图操作的方法:
-Set keySet()
:返回所有key构成的Set集合Collection values()
:返回所有value构成的Collection集合Set entrySet()
:返回所有key-value对构成的Set集合
举例:
import java.util.HashMap;
public class TestMapMethod {
public static void main(String[] args) {
//创建 map对象
HashMap map = new HashMap();
//添加元素到集合
map.put("黄晓明", "杨颖");
map.put("李晨", "李小璐");
map.put("李晨", "范冰冰");
map.put("邓超", "孙俪");
System.out.println(map);
//删除指定的key-value
System.out.println(map.remove("黄晓明"));
System.out.println(map);
//查询指定key对应的value
System.out.println(map.get("邓超"));
System.out.println(map.get("黄晓明"));
}
}
举例:
public static void main(String[] args) {
HashMap map = new HashMap();
map.put("许仙", "白娘子");
map.put("董永", "七仙女");
map.put("牛郎", "织女");
map.put("许仙", "小青");
System.out.println("所有的key:");
Set keySet = map.keySet();
for (Object key : keySet) {
System.out.println(key);
}
System.out.println("所有的value:");
Collection values = map.values();
for (Object value : values) {
System.out.println(value);
}
System.out.println("所有的映射关系:");
Set entrySet = map.entrySet();
for (Object mapping : entrySet) {
//System.out.println(entry);
Map.Entry entry = (Map.Entry) mapping;
System.out.println(entry.getKey() + "->" + entry.getValue());
}
}
Map的主要实现类:HashMap
HashMap概述
- HashMap是 Map 接口
使用频率最高
的实现类。 - HashMap是线程不安全的。允许添加 null 键和 null 值。
- 存储数据采用的哈希表结构,底层使用
一维数组
+单向链表
+红黑树
进行key-value数据的存储。与HashSet一样,元素的存取顺序不能保证一致。 - HashMap
判断两个key相等的标准
是:两个 key 的hashCode值相等,通过 equals() 方法返回 true。 - HashMap
判断两个value相等的标准
是:两个 value 通过 equals() 方法返回 true。
- **练习1:**添加你喜欢的歌手以及你喜欢他唱过的歌曲
例如:
public class SingerTest1 {
public static void main(String[] args) {
//创建一个HashMap用于保存歌手和其歌曲集
HashMap singers = new HashMap();
//声明一组key,value
String singer1 = "周杰伦";
ArrayList songs1 = new ArrayList();
songs1.add("双节棍");
songs1.add("本草纲目");
songs1.add("夜曲");
songs1.add("稻香");
//添加到map中
singers.put(singer1,songs1);
//声明一组key,value
String singer2 = "陈奕迅";
List songs2 = Arrays.asList("浮夸", "十年", "红玫瑰", "好久不见", "孤勇者");
//添加到map中
singers.put(singer2,songs2);
//遍历map
Set entrySet = singers.entrySet();
for(Object obj : entrySet){
Map.Entry entry = (Map.Entry)obj;
String singer = (String) entry.getKey();
List songs = (List) entry.getValue();
System.out.println("歌手:" + singer);
System.out.println("歌曲有:" + songs);
}
}
}
//方式2:改为HashSet实现
public class SingerTest2 {
@Test
public void test1() {
Singer singer1 = new Singer("周杰伦");
Singer singer2 = new Singer("陈奕迅");
Song song1 = new Song("双节棍");
Song song2 = new Song("本草纲目");
Song song3 = new Song("夜曲");
Song song4 = new Song("浮夸");
Song song5 = new Song("十年");
Song song6 = new Song("孤勇者");
HashSet h1 = new HashSet();// 放歌手一的歌曲
h1.add(song1);
h1.add(song2);
h1.add(song3);
HashSet h2 = new HashSet();// 放歌手二的歌曲
h2.add(song4);
h2.add(song5);
h2.add(song6);
HashMap hashMap = new HashMap();// 放歌手和他对应的歌曲
hashMap.put(singer1, h1);
hashMap.put(singer2, h2);
for (Object obj : hashMap.keySet()) {
System.out.println(obj + "=" + hashMap.get(obj));
}
}
}
//歌曲
public class Song implements Comparable{
private String songName;//歌名
public Song() {
super();
}
public Song(String songName) {
super();
this.songName = songName;
}
public String getSongName() {
return songName;
}
public void setSongName(String songName) {
this.songName = songName;
}
@Override
public String toString() {
return "《" + songName + "》";
}
@Override
public int compareTo(Object o) {
if(o == this){
return 0;
}
if(o instanceof Song){
Song song = (Song)o;
return songName.compareTo(song.getSongName());
}
return 0;
}
}
//歌手
public class Singer implements Comparable{
private String name;
private Song song;
public Singer() {
super();
}
public Singer(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Song getSong() {
return song;
}
public void setSong(Song song) {
this.song = song;
}
@Override
public String toString() {
return name;
}
@Override
public int compareTo(Object o) {
if(o == this){
return 0;
}
if(o instanceof Singer){
Singer singer = (Singer)o;
return name.compareTo(singer.getName());
}
return 0;
}
}
练习2:WordCount统计
需求:统计字符串中每个字符出现的次数
String str = “aaaabbbcccccccccc”;
提示:
char[] arr = str.toCharArray(); //将字符串转换成字符数组
HashMap hm = new HashMap(); //创建双列集合存储键和值,键放字符,值放次数
public class WordCountTest {
public static void main(String[] args) {
String str = "aaaabbbcccccccccc";
char[] arr = str.toCharArray(); // 将字符串转换成字符数组
HashMap map = new HashMap(); // 创建双列集合存储键和值
for (char c : arr) { // 遍历字符数组
if (!map.containsKey(c)) { // 如果不包含这个键
map.put(c, 1); // 就将键和值为1添加
} else { // 如果包含这个键
map.put(c, (int)map.get(c) + 1); // 就将键和值再加1添加进来
}
}
for (Object key : map.keySet()) { // 遍历双列集合
System.out.println(key + "=" + map.get(key));
}
}
}
Map实现类之二:LinkedHashMap
- LinkedHashMap 是 HashMap 的子类
- 存储数据采用的哈希表结构+链表结构,在HashMap存储结构的基础上,使用了一对
双向链表
来记录添加元素的先后顺序
,可以保证遍历元素时,与添加的顺序一致。 - 通过哈希表结构可以保证键的唯一、不重复,需要键所在类重写hashCode()方法、equals()方法。
public class TestLinkedHashMap {
public static void main(String[] args) {
LinkedHashMap map = new LinkedHashMap();
map.put("王五", 13000.0);
map.put("张三", 10000.0);
//key相同,新的value会覆盖原来的value
//因为String重写了hashCode和equals方法
map.put("张三", 12000.0);
map.put("李四", 14000.0);
//HashMap支持key和value为null值
String name = null;
Double salary = null;
map.put(name, salary);
Set entrySet = map.entrySet();
for (Object obj : entrySet) {
Map.Entry entry = (Map.Entry)obj;
System.out.println(entry);
}
}
}
Map实现类之三:TreeMap
- TreeMap存储 key-value 对时,需要根据 key-value 对进行排序。TreeMap 可以保证所有的 key-value 对处于
有序状态
。 - TreeSet底层使用
红黑树
结构存储数据 - TreeMap 的 Key 的排序:
自然排序
:TreeMap 的所有的 Key 必须实现 Comparable 接口,而且所有的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastException定制排序
:创建 TreeMap 时,构造器传入一个 Comparator 对象,该对象负责对 TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现 Comparable 接口
- TreeMap判断
两个key相等的标准
:两个key通过compareTo()方法或者compare()方法返回0。
public class TestTreeMap {
/*
* 自然排序举例
* */
@Test
public void test1(){
TreeMap map = new TreeMap();
map.put("CC",45);
map.put("MM",78);
map.put("DD",56);
map.put("GG",89);
map.put("JJ",99);
Set entrySet = map.entrySet();
for(Object entry : entrySet){
System.out.println(entry);
}
}
/*
* 定制排序
*
* */
@Test
public void test2(){
//按照User的姓名的从小到大的顺序排列
TreeMap map = new TreeMap(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof User && o2 instanceof User){
User u1 = (User)o1;
User u2 = (User)o2;
return u1.name.compareTo(u2.name);
}
throw new RuntimeException("输入的类型不匹配");
}
});
map.put(new User("Tom",12),67);
map.put(new User("Rose",23),"87");
map.put(new User("Jerry",2),88);
map.put(new User("Eric",18),45);
map.put(new User("Tommy",44),77);
map.put(new User("Jim",23),88);
map.put(new User("Maria",18),34);
Set entrySet = map.entrySet();
for(Object entry : entrySet){
System.out.println(entry);
}
}
}
class User implements Comparable{
String name;
int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public User() {
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
/*
举例:按照age从小到大的顺序排列,如果age相同,则按照name从大到小的顺序排列
* */
@Override
public int compareTo(Object o) {
if(this == o){
return 0;
}
if(o instanceof User){
User user = (User)o;
int value = this.age - user.age;
if(value != 0){
return value;
}
return -this.name.compareTo(user.name);
}
throw new RuntimeException("输入的类型不匹配");
}
}
Map实现类之四:Hashtable(古老实现类)
- Hashtable是Map接口的
古老实现类
,JDK1.0就提供了。不同于HashMap,Hashtable是线程安全的。 - Hashtable实现原理和HashMap相同,功能相同。底层都使用哈希表结构(数组+单向链表),查询速度快。
- 与HashMap一样,Hashtable 也不能保证其中 Key-Value 对的顺序
- Hashtable判断两个key相等、两个value相等的标准,与HashMap一致。
- 与HashMap不同,Hashtable 不允许使用 null 作为 key 或 value。
面试题:Hashtable和HashMap的区别
HashMap:底层是一个哈希表(jdk7:数组+链表;jdk8:数组+链表+红黑树),是一个线程不安全的集合,执行效率高
Hashtable:底层也是一个哈希表(数组+链表),是一个线程安全的集合,执行效率低
HashMap集合:可以存储null的键、null的值
Hashtable集合,不能存储null的键、null的值
Hashtable和Vector集合一样,在jdk1.2版本之后被更先进的集合(HashMap,ArrayList)取代了。所以HashMap是Map的主要实现类,Hashtable是Map的古老实现类。
Hashtable的子类Properties(配置文件)依然活跃在历史舞台
Properties集合是一个唯一和IO流相结合的集合
Map实现类之五:Properties(古老的类)
-
Properties 类是 Hashtable 的子类,该对象用于处理属性文件
-
由于属性文件里的 key、value 都是字符串类型,所以 Properties 中要求 key 和 value 都是字符串类型
-
存取数据时,建议使用setProperty(String key,String value)方法和getProperty(String key)方法
@Test
public void test01() {
Properties properties = System.getProperties();
String fileEncoding = properties.getProperty("file.encoding");//当前源文件字符编码
System.out.println("fileEncoding = " + fileEncoding);
}
@Test
public void test02() {
Properties properties = new Properties();
properties.setProperty("user","songhk");
properties.setProperty("password","123456");
System.out.println(properties);
}
@Test
public void test03() throws IOException {
Properties pros = new Properties();
pros.load(new FileInputStream("jdbc.properties"));
String user = pros.getProperty("user");
System.out.println(user);
}