java-集合二哥-Map
二哥与大哥的不同(Map接口与Collection接口下的集合存储数据的形式不同):
- Collection中的集合,元素是孤立存在的(单身狗),向集合中存储元素采用一个个元素的方式存储
- Map中的集合,元素是成对存在的(夫妻档),每个元素由键与值两部分组成,通过键可以找到对应的值
- Collection中的集合称为单列集合,Map中的集合称为双列集合
- Map(映射关系的键值对)中的集合不能包含重复的键,值可以重复,每个键只能对应一个值
- Map中常用的集合有:HashMap,LinkedHashMap
- HashMap<K,V>:存储数据采用的哈希表结构,元素的存取顺序不能保证一致,由于要保证键的唯一、不重复,需要重写键的
hashCode()
方法,equals()
方法 - LinkedHashMap<K,V>:HashMap的子类,存储数据采用的是哈希表结构+链表结构,通过链表结构可以保证元素的存取顺序一致,通过哈希表结构可以保证键的唯一、不重复,同样需要重写键的
hashCode()
方法,equals()
方法 - Tips:Map接口中的集合都有两个泛型变量<K,V>,在使用时要为两个泛型变量赋予数据类型,两个数据类型可以相同,也可以不同
Map接口中的常用方法
使用Map接口的实现类HashMap学习方法:put
get
remove
package 集合;
import java.util.HashMap;
import java.util.Map;
public class MapDemo {
public static void main(String[] args) {
/**
* 将键值对存储到集合中
* V put(K,V) K作为键的对象,V作为值的对象
* 如果存储的是重复的K,那么覆盖原有的V
* 返回值一般是null,但是当存储重复键的时候,会返回被覆盖的值
*/
function();
/**
* 1.通过键对象,获取值对象
* V get(K) ,如果集合中不存在键K,返回null
* 2.移除集合中的键值对,并返回被移除之前的值
* V remove(K) ,如果集合中不存在键K,返回null
*/
function_1();
}
private static void function_1() {
System.out.println("get方法+remove方法输出如下:-----------------");
//创建集合对象,整数作为键的对象,字符串作为值的对象
Map<Integer,String> map = new HashMap<Integer, String>();
map.put(1,"one");
map.put(2,"two");
map.put(3,"three");
System.out.println("构造的map:"+map);
String value = map.get(1);
System.out.println("get存在的键(1):"+value);
System.out.println("get不存在的键(100):"+map.get(100));
String value_removed = map.remove(1);
System.out.println("remove存在的键(1):"+value_removed);
System.out.println("remove不存在的键(100):"+map.remove(100));
System.out.println("remove后的map:"+map);
}
public static void function(){
System.out.println("put方法输出如下:-----------------");
//创建集合对象,HashMap,存储对象,键是字符串,值是整数
Map<String,Integer> map = new HashMap<String, Integer>();
Integer i = map.put("one",1);
System.out.println("一般情况下put的返回值:"+i);
map.put("two",2);
map.put("three",3);
System.out.println("构造的map:"+map);
Integer cover = map.put("three",5); //存储重复的键,将原有的值覆盖,并返回被覆盖的值
System.out.println("存储重复键的情况下,返回被覆盖的值:"+cover);
System.out.println("put重复键(three)后的map:"+map);
System.out.println();
}
}
//输出
put方法输出如下:-----------------
一般情况下put的返回值:null
构造的map:{one=1, two=2, three=3}
存储重复键的情况下,返回被覆盖的值:3
put重复键(three)后的map:{one=1, two=2, three=5}
get方法+remove方法输出如下:-----------------
构造的map:{1=one, 2=two, 3=three}
get存在的键(1):one
get不存在的键(100):null
remove存在的键(1):one
remove不存在的键(100):null
remove后的map:{2=two, 3=three}
Map集合的遍历
- 键找值:键是唯一的,通过方法
keySet()
可以得到Map集合中所有的键(Set< key >
),接着通过get(Object key)
方法,按照指定的键从Map集合中找到对应的值
package 集合;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class MapIteratorDemo {
public static void main(String[] args) {
function();
}
private static void function() {
/**
* 1.调用Map集合中的方法keySet,所有的键存储到Set集合中
* 2.遍历Set集合,获取出Set集合中的所有元素(Map中的键)
* 3.调用Map集合中的方法get,通过键获取到值
*/
//创建集合对象,HashMap,存储对象,键是字符串,值是整数
Map<String,Integer> map = new HashMap<String, Integer>();
map.put("one",1);
map.put("two",2);
map.put("three",3);
map.put("four",4);
map.put("five",5);
Set<String> set =map.keySet();
// class java.util.HashMap$KeySet $是内部类的标记,keySet返回的Set是HashMap的内部类
System.out.println(set.getClass()); //getClass()方法返回类的全名
System.out.println("Iterator遍历Set如下:-------------------------");
Iterator<String> it = set.iterator();
while(it.hasNext()){
String key = it.next();
Integer value = map.get(key);
System.out.println("key:"+key+" value:"+value);
}
System.out.println("增强for遍历Set如下:-------------------------");
for(String key:set){
Integer value = map.get(key);
System.out.println("key:"+key+" value:"+value);
}
}
}
输出如下:
class java.util.HashMap$KeySet
Iterator遍历Set如下:-------------------------
key:four value:4
key:one value:1
key:two value:2
key:three value:3
key:five value:5
增强for遍历Set如下:-------------------------
key:four value:4
key:one value:1
key:two value:2
key:three value:3
key:five value:5
Map集合中的keySet()
方法返回的Set集合类型并不是HashSet,而是java.util.HashMap$KeySet
,是HashMap的内部类
- Entry 键值对对象:在Map类设计时,提供了一个静态的内部嵌套接口(Entry),Entry将键值对的对应关系封装成了对象,即键值对对象,这样我们在遍历Map集合时,就可以从每一个键值对对象中获取对应的键与对应的值(就像夫妻双方的结婚证一样)
- 通过键值对:
entrySet()
方法得到一个包含多个键值对的Set集合(set< Entry<key,value> >
) - 接着通过Entry接口中的
getKey()
和getValue()
方法获取键和值
package 集合;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class MapIteratorDemo {
public static void main(String[] args) {
function_entry();
}
private static void function_entry() {
/**
* 1.调用Map集合中的方法entrySet,将所有的键值对对象存储到Set集合中 Set< Entry<K,V> >
* 2.遍历Set集合,获取出Set集合中的所有元素(键值对对象)
* 3.通过键值对对象方法getkey,getValue获取键值对
*/
//创建集合对象,HashMap,存储对象,键是字符串,值是整数
Map<String,Integer> map = new HashMap<String, Integer>();
map.put("one",1);
map.put("two",2);
map.put("three",3);
map.put("four",4);
map.put("five",5);
//创建内部类对象,外部类.内部类 = new
Set<Map.Entry<String,Integer>> set = map.entrySet();
System.out.println(set.getClass());
System.out.println("Iterator遍历Set如下:-------------------------");
Iterator<Map.Entry<String,Integer>> it = set.iterator();
while (it.hasNext()){
Map.Entry<String,Integer> entry = it.next();
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println("key:"+key+" value:"+value);
}
System.out.println("增强for遍历Set如下:-------------------------");
for(Map.Entry<String,Integer> entry:set){
System.out.println("key:"+entry.getKey()+" value:"+entry.getValue());
}
}
}
输出如下:
class java.util.HashMap$EntrySet
Iterator遍历Set如下:-------------------------
key:four value:4
key:one value:1
key:two value:2
key:three value:3
key:five value:5
增强for遍历Set如下:-------------------------
key:four value:4
key:one value:1
key:two value:2
key:three value:3
key:five value:5
Map集合中的entrySet()
方法返回的Set集合类型是java.util.HashMap$EntrySet
,是HashMap的内部类
Tips:增强for循环是不能直接遍历Map集合的,这里直接遍历的是Set集合,Map集合遍历的两种方式都转为了对Set集合的遍历,只能说是间接的遍历了Map集合
Map存储自定义类型数据,作为值和作为键的两种方式,当自定义类型作为键时,为了保证键的唯一性,需要重写自定义类型的hashCode()
和equals()
方法,如下
package 集合;
import java.util.HashMap;
import java.util.Map;
public class HashMapDemo {
public static void main(String[] args) {
/**
* HashMap 存储自定义的对象Person,作为值出现
* 键的对象是字符串,可以保证唯一性
*/
//function();
/**
* HashMap 存储自定义的对象Person,作为键出现
* 键的对象是Person类型,值是字符串
* 保证键的唯一性,存储到键的对象,重写hashCode和equals方法
*/
function_key();
}
public static void function_key() {
HashMap<Person,String> hashMap = new HashMap<Person,String>();
hashMap.put(new Person("张三",20),"one");
hashMap.put(new Person("李四",21),"two");
hashMap.put(new Person("王五",22),"three");
hashMap.put(new Person("王五",22),"three");
for(Person key:hashMap.keySet()){
String value = hashMap.get(key);
System.out.println("key:"+key+" value:"+value);
}
}
public static void function(){
HashMap<String,Person> hashMap = new HashMap<String, Person>();
hashMap.put("one",new Person("张三",20));
hashMap.put("two",new Person("李四",21));
hashMap.put("three",new Person("王五",22));
hashMap.put("four",new Person("林一",19));
hashMap.put("five",new Person("司徒",18));
for(String key:hashMap.keySet()){
Person value = hashMap.get(key);
System.out.println("key:"+key+" value:"+value);
}
System.out.println("--------------------------");
for(Map.Entry<String,Person> entry:hashMap.entrySet()){
String key = entry.getKey();
Person value = entry.getValue();
System.out.println("key:"+key+" value:"+value);
}
}
}
//Person类
package 集合;
/**
* 如果子类重写了父类的方法,哈希值就变成自定义的
* 存储到hashSet的依据
*/
public class Person {
private String name;
private int age;
/**
* 对象存储哈希表,去掉重复元素,依据对象自己的hashCode,equals重写目标
* 使对象中属性值name,age如果属性值相同,得到相同的哈希值
* 哈希值的计算参考String,字符串的每个字符都参与计算 31*原来的计算结果 + 字符ASCII码值
* name属性本身就是字符串,简单 name.hashCode() + age
*/
@Override
public int hashCode(){
//return name.hashCode()+age; // b,10 和 a,11 哈希值相同都是108,所以需要重写equals方法
//降低出现如上相同哈希值的概率,改进如下
return name.hashCode()+age*2;
}
@Override
public boolean equals(Object object){
if(this == object){
return true;
}
if(object == null){
return false;
}
if(object instanceof Person) { //判断一个对象是否是该类的实例
Person person = (Person) object; //是的话就可以强制转换
return name.equals(person.name) && age == person.age;
}
return false;
}
public Person(){}
public Person(String name,int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
}
}
LinkedHashMap继承HashMap,只不过它保证了迭代的顺序,即以什么顺序存进去的,就会以什么顺序取出来,用法与HashMap一致,就不做演示啦!(之前所提到的集合都是线程不安全的,运行速度快)
Map接口实现类:Hashtable,底层数据结构也是哈希表,特点和HashMap是一样的,但是需要注意的是,Hashtable是线程安全的(方法实现是同步的,开销比较大),运行速度较慢,Hashtable的命运和Vector(被ArrayList取代)是一样的,从JDK1.2开始,被更先进的HashMap所取代,除了线程是否安全这一点不同外,Hashtable是不允许存储null值和null键的,而HashMap允许存储null值和null键
虽然HashTable被抛弃了,但是他的孩子,子类Properties依然活跃在开发的舞台,Properties可以和IO流共同配合实现数据的持久存储