(1)映射键值对的形式(key和value);
(2)Map集合中,key是不能重复的,value是可以重复的;
(3)Map集合是散列存放数据的,所以存储顺序与遍历顺序是无序的,即遍历顺序可能与存储顺序不一致。
方法名称 | 说明 |
---|---|
V get(Object key) | 根据key获取值 |
Set keySet() | 获取所有健的集合 |
Collection values() | 获取所有值的集合 |
Set | 获取所有健值对象的集合 |
default V getOrDefult(Object key, V defaultValue) | 如果存在相应的key则返回其对应的value,否则返回给定的默认值defaultValue |
eg: |
public static void main(String[] args) {
Map<String, String> hashMap = new HashMap<>();
hashMap.put("qize01", "小丽");
hashMap.put("qize02", "天天");
hashMap.put("qize03", "阿奇");
hashMap.put("qize04", "毛毛");
//1、根据键获取值
System.out.println(hashMap.get("qize01"));
System.out.println(hashMap.get("ss"));
//2、获取所有健的集合
Set<String> stringSet = hashMap.keySet();
for (String key:stringSet){
System.out.println(key);
}
//3、获取所有value的集合
Collection<String> values = hashMap.values();
for (String value:values){
System.out.println(value);
}
//4、获取所有键值对象的集合
Set<Map.Entry<String, String>> entrySet = hashMap.entrySet();
for (Map.Entry<String, String> entry:entrySet){
System.out.println(entry);
}
//5、如果存在相应的key,则返回其对应的value,否则返回给定的默认值defaultValue
String qize01 = hashMap.getOrDefault("qize0001","默认值");
System.out.println(qize01);
}
1、HashMap类特点:
(1)HashMap类实现Map接口;
(2)HashMap类是散列存放数据的,所以存储顺序与遍历顺序是无序的,即遍历顺序可能与存储顺序不一致。
(3)HashMap:底层是基于数组+链表结构来实现的(JDK1.7);基于数组+链表+红黑树实现的(JDK1.8);是线程不安全的。
eg:
Map<String,String> hashMap = new HashMap<>();
hashMap.put("qize001","天天");
hashMap.put("qize002","小丽");
hashMap.put("qize003","莱德");
hashMap.put("qize004","古薇");
System.out.println(hashMap);
2、HashMap类常用方法
方法名称 | 解释 |
---|---|
V put(K key, V value) | 添加元素 |
V remove(K key, V value) | 根据健值删除对应的值 |
void clear() | 清除所有健值元素 |
boolean containsKey(Object key) | 判断集合中是否包含指定的健 |
boolean containsValue(Object value) | 判断集合中是否包含指定的值 |
boolean isEnpty() | 判断集合是否为空 |
eg:
public static void main(String[] args) {
Map<String, String> hashMap = new HashMap<>();
//put方法
hashMap.put("qize001","天天");
hashMap.put("qize002","阿奇");
hashMap.put("qize003","小丽");
System.out.println(hashMap);
System.out.println("------------------");
//判断集合中是否包含指定的健
System.out.println(hashMap.containsKey("qize002"));
//判断集合中是否包含指定的值
System.out.println(hashMap.containsValue("小丽"));
//根据健值删除对应的值
hashMap.remove("qize002");
System.out.println(hashMap);
//判断集合是否为空
System.out.println(hashMap.isEmpty());
//清除所有健值元素
hashMap.clear();
System.out.println(hashMap);
//判断集合是否为空
System.out.println(hashMap.isEmpty());
}
3、HashMap集合的遍历
(1)先取到HashMap集合中所有的键,再通过增强for循环调用get方法获取对应的value值:
eg:
public static void main(String[] args) {
HashMap<String,String> hashMap = new HashMap<>();
hashMap.put("qize01","毛毛");
hashMap.put("qize02","天天");
hashMap.put("qize03","阿奇");
Set<String> keySet = hashMap.keySet();
for (String key:keySet){
String value = hashMap.get(key);
System.out.println(key+"-"+value);
}
}
(2)使用entrySet() 方法获取所有健值对象的集合
eg:
public static void main(String[] args) {
HashMap<String,String> hashMap = new HashMap<>();
hashMap.put("qize01","毛毛");
hashMap.put("qize02","天天");
hashMap.put("qize03","阿奇");
Set<Map.Entry<String, String>> entrySet = hashMap.entrySet();
for(Map.Entry<String, String> entry:entrySet){
System.out.println(entry.getKey()+"-"+entry.getValue());
}
}
(3)使用entrySet() 方法和 Iterator 获取所有健值对象的集合
eg:
public static void main(String[] args) {
HashMap<String,String> hashMap = new HashMap<>();
hashMap.put("qize01","毛毛");
hashMap.put("qize02","天天");
hashMap.put("qize03","阿奇");
Set<Map.Entry<String, String>> entrySet = hashMap.entrySet();
Iterator<Map.Entry<String, String>> iterator = entrySet.iterator();
while (iterator.hasNext()){
Map.Entry<String, String> entry = iterator.next();
System.out.println(entry.getKey()+"-"+entry.getValue());
}
}
4、HashMap集合的底层原理
(1)HashMap 的 key 是否可以设置为自定义对象?可以。
(2)HashMap 存储数据时有序还是无序的?是无序的。
(3)HashMap 是否可以存放null 值?如果可以的话,存放在数组中哪个位置呢?可以存放null值,存放在数组index为0的位置。
(4)HashMap 集合中键值对是如何封装的?是通过实现Map接口封装的Entry对象来封装的。
eg:
public static void main(String[] args) {
//HashMap 的 key 是否可以设置为自定义对象?可以。
Student student = new Student("qize01",32);
HashMap<Student, String> hashMap1 = new HashMap<>();
hashMap1.put(student, "qize");
//HashMap 存储数据时有序还是无序的?是无序的。
HashMap<String, String> hashMap2 = new HashMap<>();
for (int i=0;i<20; i++){
hashMap2.put("qize"+i, "i:"+i);
}
hashMap2.forEach((k,v) -> {
System.out.println(k+","+v);
});
//HashMap 是否可以存放null 值?如果可以的话,存放在数组中哪个位置呢?
//可以存放null值,存放在数组index为0的位置
HashMap<String, String> hashMap3 = new HashMap<>();
hashMap3.put(null,"毛毛");
}
(5)key的hash算法原理
hashMap集合 1.7版本 是基于数组+链表实现的
hashMap集合 1.8版本 是基于数组+链表+红黑树实现的
根据key的hashCode 取余entrys.length,得到的余数就是该key存放在我们数组中对应的index位置
eg:
public class QizeHashMap<K, V> {
private Entry[] entrys = new Entry[10000];
class Entry<K, V> {
K k;//key
V v;//value
int hash;//key的hash值
public Entry(K k, V v) {
this.k = k;
this.v = v;
}
}
public void put(K k, V v){
//根据key的hashCode 取余entrys.length
//得到的余数就是该key存放在我们数组中对应的index位置
int index = k.hashCode() % entrys.length;
entrys[index] = new Entry<>(k, v);
}
public V get(K k){
//如果根据key查询的话,hashCode 取余entrys.length
int index = k.hashCode() % entrys.length;
return (V) entrys[index].v;
}
public static void main(String[] args) {
QizeHashMap<Object, Object> hashMap = new QizeHashMap<>();
hashMap.put("qize", "毛毛");
System.out.println(hashMap.get("qize"));
}
}
(6)什么是hash冲突问题?
hashCode相同但是值不同,例如 key=“a” 和key=97 最终计算的存放value的下标index相同,则key=97的存放在数组的键值对(Entry对象)会覆盖掉key="a"存放在数组中的键值对。
eg:
//在(5)key的hash算法原理的例子的基础上修改main方法,如下
public static void main(String[] args) {
QizeHashMap<Object, Object> hashMap = new QizeHashMap<>();
hashMap.put("a", "毛毛");
hashMap.put(97, "小丽");
System.out.println(hashMap.get("a"));
}
运行得到的打印结果为 “小丽” ,而不是 “毛毛” ,这就是hash冲突问题。
怎么解决hash冲突问题?
在JDK 1.7中是通过链表来解决的。
先判断该index位置 是否有存放键值对(Entry对象),如果取出结果为null,则表示此index位置没有存放数据,则可以直接存放,如果取出的结果为一个Entry对象,则表示此处有hash冲突,需要进行处理。
故上述“(5)key的hash算法原理的例子”可以优化为如下:
public class QizeHashMap<K, V> {
private Entry[] entrys = new Entry[10000];
class Entry<K, V> {
K k;//key
V v;//value
int hash;//key的hash值
Entry<K, V> next;//Entry的下一个节点
public Entry(K k, V v, int hash) {
this.k = k;
this.v = v;
this.hash = hash;
}
}
public void put(K k, V v){
//根据key的hashCode 取余entrys.length
//得到的余数就是该key存放在我们数组中对应的index位置
/**
* 1、先判断该index位置 是否有存放数据
* 2、如果取出结果为null,则表示此index位置没有存放数据,则可以直接存放
* 3、如果取出的结果为一个Entry对象,则表示此处有hash冲突,需要进行处理
*/
int hash = k.hashCode();
int index = hash % entrys.length;
Entry oldEntry = entrys[index];
if(oldEntry == null){
entrys[index] = new Entry<>(k, v, hash);
}else {
oldEntry.next = new Entry<>(k, v, hash);
}
}
public V get(K k){
//如果根据key查询的话,hashCode 取余entrys.length
int hash = k.hashCode();
int index = hash % entrys.length;
for (Entry<K, V> entry = entrys[index]; entry != null; entry = entry.next){
if(entry.hash == hash && (entry.k == k || entry.k.equals(k))){
return entry.v;
}
}
return null;
}
public static void main(String[] args) {
QizeHashMap<Object, Object> hashMap = new QizeHashMap<>();
hashMap.put("a", "毛毛");
hashMap.put(97, "小丽");
System.out.println(hashMap.get("a"));
}
}
eg:
public class Student {
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
public static void main(String[] args) {
/**
* equals 属于 object父类中,默认的情况下 在比较两个对象的内存地址是否相同
* 两个字符串equals 是比较两个对象的值是否相等,因为String类重写了equals方法
*/
String str1 = "qize";
String str2 = "qize";
System.out.println(str1.equals(str2));
System.out.println("--------------------");
Student student1 = new Student("qize",22);
Student student2 = new Student("qize",22);
System.out.println(student1.equals(student2));
System.out.println(student1.hashCode());
System.out.println(student2.hashCode());
}
LinkedHashMap 继承自HashMap,它的多种操作都是建立在HashMap操作的基础上的,与HashMap不同的是,LinkedHashMap 维护了一个Entry 的双向链表,保证了插入的Entry 中的顺序。
eg:
public static void main(String[] args) {
LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>();
for(int i=1; i<=100; i++){
linkedHashMap.put("k:"+i,"v:"+i);
}
for (Map.Entry<String, String> entry:linkedHashMap.entrySet()){
System.out.println(entry.getKey()+","+entry.getValue());
}
}