1.概述
生活中经常有一一对应的关系,例如:ip地址和主机名,身份证号与姓名,系统用户名与系统用户对象等等,这种关系可以叫做映射。
Java提供了专门的集合来存放这种对应关系的对象,也就是java.util.Map接口
Map接口下的集合,Collection接口下的集合,存储形式不同。
Map接口的结合都需要指定两个泛型K,V 这两个泛型可以数据类型相同,也可以不同。
-
Collection中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个一个元素的方式存储。
-
Map中的集合,元素是成对出现的。每个元素由键和值两个部分组成,通过键可以找到对应的值。
-
Collection中的集合称之为单列集合,Map中的集合称之为双列集合。
-
注意: Map中集合不能包含重复的键,值可以重复,每个键只能对应一个值。
2.Map的常用子类
通过查看Map接口发现有很多子类,这里我们主要讲常用的HashMap集合、LinkedHashMap集合。
-
HashMap<K,V>:存储数据采用的哈希表结构,元素的存取顺序不能保证一致,由于要保证键的唯一,不重复,需要重写键的hashCode方法、equals方法。
-
LikedHashMap<K,V>:HashMap的子类LinkedHashMap,存储数据结构采用哈希表+链表的结构,通过链表可以保证元素的存取顺序一致;通过哈希表可以保证键的唯一、不重复,需要重写键的hashCode方法、equals方法。
3.Map中的常用方法
public V put(K key, V value);添加元素到集合,使用put方法如果指定键在集合中不存在,返回null,存在则返回替换前的值,添加重复键时新的键会覆盖老数据,返回老数据的值。
public V remove(Object key);删除键为K的元素,使用remove方法如果指定键在集合中不存在,返回null,存在则返回该键映射的值。
public V get(Object key); 拿到键查找对应的值,返回该键映射的值。
public boolean containsKey(Object key)判断该集合中是否有这个键。
public boolean containsValue(Object value)判断该集合中是否有这个值。
代码演示:
public class Demo01 {
public static void main(String[] args) {
// 创建Map对象
HashMap<String,String> map=new HashMap<>();
// 添加元素到集合 map.put(key,value)
//添加重复键时新的键会覆盖老数据
// 使用put方法如果指定键在集合中不存在,返回null,存在返回替换前的值
String str = map.put("007", "张飞");
System.out.println("str = "+str);
map.put("008","关羽");
String str1 = map.put("009", "刘备");
System.out.println("str1 = "+str1);
String str2 = map.put("009", "赵云");
System.out.println("str2 = "+str2);
map.put("010","张飞");
System.out.println("map = "+map);
// 删除 map.remove(key)
String remove = map.remove("002");
System.out.println("remove = "+remove);
String remove1 = map.remove("007");
System.out.println("remove1 = "+remove1);
System.out.println("map = "+map);
// 查看 拿到键查找对应的值
System.out.println(map.get("008"));
// 判断该集合中是否有这个键
System.out.println(map.containsKey("009"));
// 判断该集合中是否有这个值
System.out.println(map.containsValue("张飞"));
}
}
4.Map集合的遍历
假设对如下Map集合遍历:
HashMap<Integer,String> hashMap=new HashMap<>();
hashMap.put(1,"aaa");
hashMap.put(2,"bbb");
hashMap.put(3,"ccc");
hashMap.put(4,"ddd");
hashMap.put(5,"eee");
方式1:键找值的方式
-
public Set<K> keySet(): 获取map集合中的所有键,存储到set集合中。
步骤:
1.获取Map中所有的键,由于键是唯一的,所以返回到一个set集合中。
2.用增强for遍历键的set集合,得到每一个键.
3.根据键,获取键所对应的值.
代码演示:
Set<Integer> set=hashMap.keySet();//将Map中的键存到Set中
for (Integer integer : set) {
System.out.println(integer+" : "+hashMap.get(integer));
}
方式2:键值对方式
通过集合中的每个键值对(Entry)对象,获取键值 对(Entry)对象中的键与值.
Entry键值对对象:
Map中的键与值是一一对应的,这一对对象在map中称之为一个Entry.Entry将键值对的对应关系封装成了对象,也就是键值对对象,我们可以在遍历map时获取对应的所有键值对对象.
-
public Set<Map.Entry<K,V>> entrySet();获取map集合中的所有键值对对象的集合(Set集合);
获取了Entry对象表示获取了一对键值对,Entry中也提供了获取键和获取值的方法;
-
public K getKey():获取Entry对象中的key.
-
public V getValue:获取Entry对象中的value.
遍历步骤:
-
获取Map集合中所有键值对对象,以set集合形式返回.方法: entrySet();
-
遍历包含键值对的set集合 得到每一个entry对象.
-
通过entry对象,获取entry对象中的键和值.方法:getKey() , getValue();
eg1:
Set<Map.Entry<Integer, String>> entries = hashMap.entrySet();
for (Map.Entry<Integer, String> entry : entries) {
System.out.println(entry.getKey()+" : "+entry.getValue());
eg2:
// 遍历方式2.1 使用迭代器 hashMap没有迭代器,先转为键值对的Set集合再使用迭代器方式遍历
Iterator<Map.Entry<Integer, String>> iterator = hashMap.entrySet().iterator();
while (iterator.hasNext()){
Map.Entry<Integer, String> next = iterator.next();
System.out.println(next.getKey()+" : "+next.getValue());
}
5.Map集合练习
需求:
计算一个字符串中每个字符出现的次数.
分析:
-
获取一个字符串对象
-
创建一个Map集合 思考key , value 分别是什么
-
遍历字符串 得到每个字符
-
判断map中是否存过这个字符
-
如果没有 就是第一次出现 ,存次数为1, 如果有 说明出现过 次数++.
-
最终打印结果.
public class Demo03 { public static void main(String[] args) { System.out.println("请输入一个字符串"); String str = new Scanner(System.in).next(); // 定义一个 每个字符出现字数的方法 findChar(str); } private static void findChar(String str) { // 1: 创建一个集合 存储字符 以及出现的次数。 Map<Character, Integer> map = new HashMap<>(); for (int i = 0; i < str.length(); i++) { // 2: 遍历字符串。 char c = str.charAt(i); // 3. 判断该字符是否出现在集合中 if (!map.containsKey(c)) {//不包含说明没有出现过这个字符 就是第一次出现 map.put(c, 1); }else {// 包含代表出现过 获取之前的次数然后+1 Integer count = map.get(c); map.put(c,++count); } } System.out.println("map = " + map); } }
6.HashMap存储自定义类型(无序且唯一)
练习: 每个学生都有自己的姓名,年龄,有对应关系,将学生对象和家庭住址存入到map集合中,学生为键,住址为值.
注意: 学生姓名和年龄相同视为同一个学生.(所以得重写equals和hashCode方法)这里对学生类的创建不予展示了
public class Demo04 { public static void main(String[] args) { // 1.创建一个map集合。 Map<Student,String> map =new HashMap<>(); // 2.添加元素 map.put(new Student("zhang3",22),"哈尔滨"); map.put(new Student("zhang3",22),"重庆"); map.put(new Student("zhang4",33),"四川"); map.put(new Student("zhang5",22),"广东"); // 3.遍历集合, 键找值的方式。 Set<Student> students = map.keySet(); for (Student student : students) { String value = map.get(student); System.out.println(student+" : "+value); } } }
7.HashMap综合练习
-
设计一个HashMap来存储购物车种的商品, 其中K为商品编号(新增时动态获取),V为商品对象(包含商品名称、价格和数量属性);
-
实现功能: a.添加商品到购物车(根据商品编号和商品) b.更新购物车中商品的数量 c.从购物车中移除商品(根据商品编号) d.计算购物车中所有商品的总价 e.打印购物车中所有商品的信息 要求使用java的异常处理机制处理可能出现的问题,如添加重复商品 、移除不存在的商品等。 并测试。
-
代码演示(Product类不在此写明):
public class ShoppingCar { public Map<String, Product> car; public ShoppingCar() { this.car = new HashMap<>(); } // a.添加商品到购物车(根据商品编号和商品) public void addProduct(String productId, Product p) throws Exception { if (car.containsKey(productId)){ throw new Exception("商品已经存在,添加异常"); }else { car.put(productId,p); } } // b.更新购物车中商品的数量 public void updateProduct(String productId,int newCount) throws Exception { if (!car.containsKey(productId)){ throw new Exception("商品不存在,异常"); }else { car.get(productId).setNum(newCount);//找到对应编号的商品并对商品数量重新赋值 } } // c.从购物车中移除商品(根据商品编号) public void remove(String productId) throws Exception { if (car.containsKey(productId)){ car.remove(productId); }else { throw new Exception("商品不存在,移除异常"); } } // d.计算购物车中所有商品的总价 public void totalPrice ( ){ double sum=0.0; Set<Map.Entry<String, Product>> entries = car.entrySet(); for (Map.Entry<String, Product> entry : entries) { Product value = entry.getValue(); sum+=value.getPrice()*value.getNum(); } System.out.println("当前购物车商品总结"+sum); } // e.打印购物车中所有商品的信息 public void printProduce(){ Set<Map.Entry<String, Product>> entries = car.entrySet(); for (Map.Entry<String, Product> entry : entries) { System.out.println("id:"+entry.getKey()); System.out.println("name:"+entry.getValue().getName()); System.out.println("price:"+entry.getValue().getPrice()); System.out.println("num:"+entry.getValue().getNum()); } } }
测试类:
public class Test { public static void main(String[] args) { Product p1=new Product("可口可乐",3,2); Product p2=new Product("乐事薯片",6.9,3); Product p3=new Product("豪士面包",3,5); ShoppingCar car=new ShoppingCar(); try { car.addProduct("001",new Product("可口可乐",3,2)); car.addProduct("002",new Product("乐事薯片",6.9,3)); car.addProduct("003",new Product("豪士面包",3,5)); car.updateProduct("001",6); car.totalPrice(); car.remove("002"); car.printProduce(); }catch (Exception e){ e.printStackTrace(); } } }
-
8.LinkedHashMap介绍(有序且唯一)
HashMap下有一个子类LinkedHashMap,它是链表和哈希表组合的一个数据存储结构.
public class Demo03 { public static void main(String[] args) { // 1.创建集合对象 有序 Map<String,String> map=new LinkedHashMap<>(); map.put("zhang3","777"); map.put("li4","888"); map.put("wang5","99"); map.put("zhang6","333"); // 2.遍历 Set<Map.Entry<String, String>> entries = map.entrySet(); for (Map.Entry<String, String> entry : entries) { System.out.println(entry.getKey()+" : "+entry.getValue()); } } }