Java集合之Map集合知识点汇总(重点)

431 阅读7分钟

Map集合

这是我参与8月更文挑战的第21天,活动详情查看:8月更文挑战

map的集合形式是键值对的方式;

其常用方法:

Modifier and TypeMethodDescription
Vget(Object key)Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.
Vput(K key, V value)Associates the specified value with the specified key in this map (optional operation).
static <K,V> Map<K,V>of()Returns an unmodifiable map containing zero mappings.

其中Map.of()可以将每一组数据转为map进行保存;

使用Map保存Key-Value数据:

package Java从入门到项目实战.Java类集框架.Map集合;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
public class 保存Key_Value数据 {
    public static void main(String[] args) {
//        如果Key重复  则会报错:java.lang.IllegalArgumentException
//        如果Value与Key设置为null,则会报错:java.lang.NullPointerException
        Map<String,Integer> map = Map.of("one",1,"two",null);
        System.out.println(map);
//        System.out.println(map.get("one"));
//        Put只能在map子类中使用
    }
}

注意点:

  1. 如果Key重复 则会报错:java.lang.IllegalArgumentException
  2. 如果Value与Key设置为null,则会报错:java.lang.NullPointerException

HashMap子类:

特点:采用散列方式保存数据,即无序排列

继承结构:

public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable

HashMap进行map集合的操作:

package Java从入门到项目实战.Java类集框架.Map集合;
import java.util.HashMap;
import java.util.Map;
public class HahMap子类 {
    public static void main(String[] args) {
        Map<String,Integer> map =  new HashMap<String,Integer>();
//        进行Map的集合操作
//        map.put("One",1);
//        map.put("Two",2);
//        map.put("Three",3);
//        System.out.println(map);

//        Map数据的保存方法:
        System.out.println(map.put("One",1)); //保存数据,但是保存的数据的key不存在,返回null
        System.out.println(map.put("One",101));  // 返回1,也就是返回的是覆盖掉的值

        map.put("Three",3);
        map.put("demo1",4);
        map.put("demo2",null);
        map.put("Te",6);
        map.put(null,7);
        /*结果
        * null、7
        * */
        System.out.println(map.get("demo2"));
        System.out.println(map.get(null));
    }
}

注意两个输出的注释:

//Map数据的保存方法:
System.out.println(map.put("One",1)); //保存数据,但是保存的数据的key不存在,返回null
System.out.println(map.put("One",101));  // 返回1,也就是返回的是覆盖掉的值

在使用Map保存数据的时候Key与Value都不能使用null,但是使用HashMap进行保存数据可以将Key或Value设置为null,当然也可以Key=Value=null,但是这样的实现保存毫无意义。

put方法在发生覆盖钱都可以返回原始内容,这样就可以依据返回结果来判断所设置的key是否存在;

HashMap数据扩充操作原理分析:

1) 首先观察构造方法:

设置数据扩充阈值;

public HashMap() {
    // all other fields defaulted
    this.loadFactor = DEFAULT_LOAD_FACTOR; 
}

然后跳转DEFAULT_LOAD_FACTOR查看:

作用:容量的扩充阈值

/**
     * The load factor used when none specified in constructor.
     */
static final float DEFAULT_LOAD_FACTOR = 0.75f;

通过源码可以发现,每个Hashmap在对象实例化的时候都已经考虑到了数据存储的扩充问题;

2) 观察HashMap中的put方法

public V put(K key,V value){
    return putVal(hash(key),key,value,false,true);
}

在使用put方法进行数据保存的时候会调用putVal方法,同时会将key进行哈希处理(生成hash码)

而putVal()方法为了方便数据保存会将数据封装为一个Node节点类对象,而在使用putVal()方法的操作过程会调用reasize()方法进行扩容;

3)容量扩充

当保存的数据超过既定的存储容量则会进行扩容,原则如下:

常量地址:DEFAULT_INITIAL_CAPACITY;作为初始化的容量配置,而后1向左移4为-》16;

常量的默认大小为16个元素,也就是说默认可以保存的最大的内容是16;

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

当保存的数据内容超过了设置的阈值DEFAULT_LOAD_FACTOR = 0.75f

相当于容量x阈值 = 16*0.75 = 12;即保存到12个元素的时候就会进行容量扩充;

扩充的模式是2倍扩充:即每一次扩充2倍的容量。

4)大数据下的数据存储方式:

在JDK1.8之后来到大数据时代,这就触发了HashMap在大数据量中的访问效率问题;

其中提供了一个重要的常量:TREEIFY_THRESHOLD

static final int TREEIFY_THRESHOLD = 8;

在使用HashMap进行保存的时候,如果保存的数据个数没有超过阈值8,那么会按照链表的形式进行数据的存储;而超过了这个阈值,则会将链表转为红黑树以实现树的平衡;并且利用左旋与右旋保证数据的查询性能。

LinkedHashMap子类:

特点:基于链表形式实现偶对的存储,可以保证存储顺序与数据增加的顺序相同;

继承结构:

public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>

使用LinkedHashMao子类存储数据:

package Java从入门到项目实战.Java类集框架.Map集合;
import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashMap子类存储数据 {
    public static void main(String[] args) {
        Map<String,Integer> map = new LinkedHashMap<String,Integer>();
        map.put("张三",1);
        map.put("李四",2);
        map.put("王五",3);
        map.put(null,3);
        map.put("赵六",null);
        System.out.println(map);
    }
}

运行可以发现集合的保存的顺序与数据增加顺序相同;同时LinkedHashMap子类允许保存的Key或value内容为null;

Hashtable子类:

其继承结构如下:

public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, Serializable

使用Hashtable子类保存数据:

package Java从入门到项目实战.Java类集框架.Map集合;
import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashMap子类存储数据 {
    public static void main(String[] args) {
        Map<String,Integer> map = new Hashtable<String,Integer>();
        map.put("张三",1);
        map.put("李四",2);
        System.out.println(map);
    }
}

HashMap与Hashtable的区别:

HashMap中的 方法都是异步操作(非线程安全),HashMap中允许保存null数据

Hashtable中的方法都是同步操作(线程安全),但是效率慢,Hashtable不允许保存Null数据;否则会出现NUllpointException;

TreeMap子类:

特点:TreeMap属于有序的Map集合类型;它可以按照key进行排序;所以需要Comaprable接口配合;

继承结构如下:

public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, Serializable

TreeMap子类进行数据Key的排序:

package Java从入门到项目实战.Java类集框架.Map集合;
import java.util.Map;
import java.util.TreeMap;
public class TreeMap子类进行数据Key的排序 {
    public static void main(String[] args) {
//        因为该程序保存的Key属于String类型,由于String中实现了Comparable接口,所以
//        可以根据保存的字符的编码进行由低到高的进行排序
        /*
        * public final class String
            implements java.io.Serializable, Comparable<String>, CharSequence
        *
        *
        * */
        Map<String,Integer> map = new TreeMap<String,Integer>();
        map.put("C",3);
        map.put("B",2);
        map.put("A",1);
        System.out.println(map);

    }
}

Map.Entry内部接口:

在JDK1.9开始可以利用Map接口中创建Map.entry内部接口实例;

package Java从入门到项目实战.Java类集框架.Map集合;
import java.util.Map;
public class Map_Entry内部接口 {
    public static void main(String[] args) {
        Map.Entry<String,Integer> entry = Map.entry("One",1);
        System.out.println(entry.getKey());
        System.out.println(entry.getValue());
        //观察使用的子类
        System.out.println(entry.getClass().getName());
    }
}

在程序继续宁Map.Entry对象构建的时,只传入Key和Value就会自动利用KeyValueHolder子类实例化Map.Entry接口对象。

Iterator输出Map集合:

集合数据输出的标准形式是基于Iterator接口完成的;Collection接口直接提供iterator方法可以获得iterator接口实例;但由于Map接口中保存的数据是多个Map.Entry接口封装的二元偶对象,所以就必须采用Map集合的迭代输出;

  1. 使得Map接口中的entrySet(),将Map集合变为Set集合;
  2. 取得Set接口实例后就可以利用iterator方法取得iterator的实例化对象;
  3. 使用iterator迭代找到每一个Map.Entry对象,并进行Key与Value的分。

Iterator和foreach输出Map集合:

package Java从入门到项目实战.Java类集框架.Map集合;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Map集合的输出问题 {
    public static void main(String[] args) {
        Map<String,Integer> map = new HashMap<String,Integer>();
        map.put("One",1);
        map.put("two",2);
//        输出的map中的Key与Value的值
        Set<Map.Entry<String, Integer>> set = map.entrySet();//将Map集合转变为Set集合
        Iterator<Map.Entry<String,Integer>> iter = set.iterator();  //获取Iterator接口
        while(iter.hasNext()){
//          Set中存的都是Map.Entry()对象
            Map.Entry<String, Integer> me = iter.next();
            System.out.print(me.getKey()+" "+me.getValue());
        }
//        System.out.println("\n");
        //foreach循环输出Map集合:
        for(Map.Entry<String,Integer> entry:set){
            System.out.print(entry.getKey()+" "+entry.getValue());
        }
    }
}

自定义Key类型:

采用自定义类的形式实现,但是作为Key类型的类由于存在数据查找的需求,所以必须在类中覆写hashcode()与equals()方法。

package Java从入门到项目实战.Java类集框架.Map集合;
import java.util.HashMap;
import java.util.Map;
class Member{
    private String name;
    private int age;

    public Member(String name,int age){
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Member)) return false;

        Member member = (Member) o;

        if (age != member.age) return false;
        if (name != null ? !name.equals(member.name) : member.name != null) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }
}
/*
* Key进行自定义;作为Key类型的类由于存在数据查找的需求,所以应该在类中覆写HashCode() 和equals();
* */
public class 自定义Key的值 {
    public static void main(String[] args) {
        Map<Member,String> map = new HashMap<Member,String>();
        map.put(new Member("张三",22),"xbhog");
        map.put(new Member("李四",23),"博客");
        map.put(new Member("王五",26),"welcome");
        System.out.println(map.get(new Member("张三",22)));

    }
}

在存储大量的数据中,key还是可能出现重复的问题,这个问题叫Hash冲突;

解决的方式:

链地址法(拉链法)、开放寻址、再哈希、建立公共溢出区;

结束:

如果你看到这里或者正好对你有所帮助,希望能点个👍或者⭐感谢;

有错误的地方,欢迎在评论指出,作者看到会进行修改。