Java 集合 —— Map

一、Map接口

映射(map)是一个存储键、值对的对象。给定一个键,可以查询得到他的值,键和值都是对象(基础类型不可以)。

键必须是唯一的,值是可以重复的。

有些映射可以接收null键和null值,而有的不行

下面的接口支持映射:

接口描述
Map映射唯一关键字给值
Map.Entry映射中的元素(关键字、值对),这是Map的内部类
SortedMap扩展Map一遍按关键字升序保持

Map接口定义的方法:

V put(K key, K value) 添加键值对(如果已存在键,则返回历史值,否则返回null)

void clear() 清空map

boolean containsKey(Object key) 判断map中是否包含某个 key

boolean containsValue(Object value) 判断map中是否包含某个 value

V get(Object key) 通过key获取value

boolean isEmpty() 判断map中元素个数是否为0

Set keySet() 获取map集合所有的key

V remove(Object key) 通过key删除键值对

int size() 获取map中所有键值对个数

Collections values() 获取map集合所有的value

Set<Map.Entry<K,V>> entrySet() 将map集合转换成Set集合

 

二、HashMap

HashMap类时基于哈希表的map接口的实现,并允许使用null键和null值

构造方法:

HashMap()默认构造方法。数组大小为16.
HashMap(Map m)构造方法。并使用给定map进行赋值
HashMap(int capacity)构造方法。给定数组的初始大小。
HashMap(int capacity,float fillRatio)构造方法。给定数组的初始大小和扩容因子。

 

散列映射不保证它的元素的顺序。元素加入散列映射的顺序并不一定是他们被迭代读出的顺序。

自定义类作为HashMap的key的时候,规定要重写类的hashCode() 和 equals() 两个方法。

HashMap通过key的hashCode() 和 equals() 两个方法判断key是否相同,如果key的hashCode()方法相同且equals()也相等,那么HashMap就认为key是相等的。

 

@AllArgsConstructor
    static class Student{
        private String name;
        private Integer 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 Objects.equals(name, student.name) &&
                    Objects.equals(age, student.age);
        }

        @Override
        public int hashCode() {
            return Objects.hash(name, age);
        }
    }

    @Test
    public void hashMapTest(){
        Student stu1 = new Student("唐三",25);
        Student stu2 = new Student("唐昊",70);
        Student stu3 = new Student("唐三",25);


        Map<Student, String> studentMap = new HashMap<>();
        studentMap.put(stu1, "唐三");
        studentMap.put(stu2, "唐昊");
        studentMap.put(stu3, "唐三2");

        System.out.println(studentMap);




    }



输出结果:
{Student{name='唐昊', age=70}=唐昊, Student{name='唐三', age=25}=唐三2}

上面的实例中,如果Student类没有重写hashCode() 和 equals() 两个方法,那么就会输出3个元素,因为Object默认的equals()方法对比的是对象的地址,而两个对象的地址是完全不一样的。

三、TreeMap

TreeMap类通过使用红黑树实现Map接口

TreeMap提供按排序顺序存储键值对的有效手段,同时允许快速检索(红黑树查询速度非常快)。

不像散列映射,树映射保证它的元素按关键字升序排序

TreeMap 的构造方法:

TreeMap()默认构造方法
TreeMap(Comparator comp)传入自定义的Comparator
TreeMap(Map m) 
TreeMap(SortedMap sm) 

 

TreeMap实现SortedMap并且扩展AbstractMap,它本身并没有定义其他方法

TreeMap使用Comparator的compare方法(或者key所属类型实现Comparable接口的compareTo方法)判断key是否相等,key是否相等与hashCode() 和 equals() 两个方法没有任何关系,如果compare()或者compareTo()方法返回0就表示key是相等的。

在创建TreeMap的时候,要么传入Comparator的实现类,要么key的所属类实现Comparable的compareTo方法,二者必须选择一个;且如果二者同时满足,会选择传入的Comparator的实现类进行key的比较。

Comparator#compare:

Comparable#compareTo:

 

public V put(K key, V value) {
        Entry<K,V> t = root;
        if (t == null) {
            compare(key, key); // type (and possibly null) check

            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator;
        if (cpr != null) {
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        else {
            if (key == null)
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
                Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                parent = t;
                cmp = k.compareTo(t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }

 

示例1:

    @Test
    public void TreeMapTest(){
        Student stu1 = new Student("唐三",25);
        Student stu2 = new Student("唐昊",70);
        Student stu3 = new Student("唐三",25);


        Map<Student, String> studentMap = new TreeMap<>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                return 0;
            }
        });
        studentMap.put(stu1, "唐三");
        studentMap.put(stu2, "唐昊");
        studentMap.put(stu3, "唐三2");

        System.out.println(studentMap);
    }

输出结果:
{Student{name='唐三', age=25}=唐三2}

注意:上面实例中,Comparator的compare方法虽然实现了,但是没有任何逻辑,直接返回0,所以在put的时候,只有第一个stu1作为key可以存入,剩余的stu2和stu3被认为是和stu1相等的key,所以只是进行了值的替换,而没有新的键值对存储。

修改示例1,示例2:

    @Test
    public void TreeMapTest(){
        Student stu1 = new Student("唐三",25);
        Student stu2 = new Student("唐昊",70);
        Student stu3 = new Student("唐三",25);


        Map<Student, String> studentMap = new TreeMap<>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                return s1.getName().compareTo(s2.getName());
            }
        });
        studentMap.put(stu1, "唐三");
        studentMap.put(stu2, "唐昊");
        studentMap.put(stu3, "唐三2");

        System.out.println(studentMap);
    }

输出结果:
{Student{name='唐三', age=25}=唐三2, Student{name='唐昊', age=70}=唐昊}

 上面示例中stu1和stu2作为key存入了映射中,stu3没有作为key被存入,因为stu3被认为和stu1是相等的key,所以只进行了值的替换。key比较相等时会替换当前值。

示例3:Student类实现Comparable接口

    @Getter
    @AllArgsConstructor
    static class Student implements Comparable<Student>{
        private String name;
        private Integer 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 Objects.equals(name, student.name) &&
                    Objects.equals(age, student.age);
        }

        @Override
        public int hashCode() {
            return Objects.hash(name, age);
        }

        @Override
        public int compareTo(Student o) {
            return this.getName().compareTo(o.getName());
        }
    }


    @Test
    public void TreeMapTest(){
        Student stu1 = new Student("唐三",25);
        Student stu2 = new Student("唐昊",70);
        Student stu3 = new Student("唐三",25);


        Map<Student, String> studentMap = new TreeMap<>();
        studentMap.put(stu1, "唐三");
        studentMap.put(stu2, "唐昊");
        studentMap.put(stu3, "唐三2");

        System.out.println(studentMap);
    }

输出结果:
{Student{name='唐三', age=25}=唐三2, Student{name='唐昊', age=70}=唐昊}

 

 

 

 

 

 

 

 

三、LinkedHashMap

LinkedHashMap 继承自 HashMap,在 HashMap 基础上,通过维护一条双向链表,解决了 HashMap 不能随时保持遍历顺序和插入顺序一致的问题。在一些场景下,该特性很有用,比如缓存。

主要特点:可以保持存入的顺序和遍历的顺序一致(HashMap的遍历顺序不可预知,且会在put新数据的时候发生变动),其余特性与HashMap是一样的。

    @Test
    public void linkedHashMapTest(){
        Student stu1 = new Student("唐三",25);
        Student stu2 = new Student("唐昊",70);
        Student stu3 = new Student("唐三",25);
        Student stu4 = new Student("比比东",50);
        Student stu5 = new Student("波塞西",200);


        Map<Student, String> studentMap = new LinkedHashMap<>();
        studentMap.put(stu1, "唐三");
        studentMap.put(stu2, "唐昊");
        studentMap.put(stu3, "唐三2");
        studentMap.put(stu4, "比比东");
        studentMap.put(stu5, "波塞西");

        for(Student stu : studentMap.keySet()){
            System.out.println(stu + "----------" + studentMap.get(stu));
        }

    }

输出结果:
Student{name='唐三', age=25}----------唐三2
Student{name='唐昊', age=70}----------唐昊
Student{name='比比东', age=50}----------比比东
Student{name='波塞西', age=200}----------波塞西

 

 

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值