EchoDemo's Blogs

Java中的Map

1、Map接口详解

(1)映射(map)是一个存储键/值对的对象。给定一个键,可以查询到它的值,键和值都是对象。

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

(3)有些映射可以接收null键和null值,而有的不能。

(4)Map接口定义的方法:int size();boolean isEmpty();boolean containsKey(Object key);boolean containsValue(Object value);V get(Object key);V put(K key,V value);V remove(Object key);Collection values();Set<Map.Entry<K,V>> entrySet();返回包含的映射关系的Set视图,Map接口定义的entrySet()方法返回包含映射项Entry的集合(Set),集合中元素是Map.Entry类型。

(5)Map.Entry接口代表映射项(键-值对)类型,是Map的嵌套类型(是Map的内部类)。

(6)Map.Entry接口定义的方法:K getKey();V getValue();V setValue(V value)。

2、HashMap及常用API

(1)HashMap类是基于哈希表的map接口的实现,并允许使用null键和null值。

(2)构造方法:HashMap();HashMap(Map m);HashMap(int capacity);HashMap(int capacity,float fillRatio)。

(3)HashMap实现并扩展AbstractMap,本身并没有增加任何新的方法。

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

(5)HashMap常用方法举例

package com.iotech.map;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;

public class HashMapDemo1 {

    public static void main(String[] args) {
        //HashMap<String,String> hashmap=new HashMap<String, String>();//构造一个空的 HashMap ,默认初始容量(16)和默认负载系数(0.75)。 
        HashMap<String,String> hashmap=new HashMap<String, String>(1);//构造一个空的 HashMap具有指定的初始容量和默认负载因子(0.75)。 
        hashmap.put("jay", "张三");
        hashmap.put("jay", "李四");
        hashmap.put("rose", "玫瑰");
        hashmap.put("Mary", "小红");
        System.out.println(hashmap);

        //获取map中的所有键
        /*Set<String> keysSet=hashmap.keySet(); 
        for(String name:keysSet){
            System.out.println(name);
        }*/

        //获取map中的所有值
        Collection<String> values=hashmap.values();
        for(String value:values){
            System.out.println(value);
        }

        //得到key的同时得到key所对应的值
        Set<String> keysSet=hashmap.keySet(); 
        for(String name:keysSet){
            System.out.println(name+"-->"+hashmap.get(name));
        }

        System.out.println(hashmap.size());
        System.out.println(hashmap.isEmpty());

        //当我们调用put(key,value)方法的时候,首先会把key和value封装到Entry这个静态内部类对象中。
        //把Entry对象再添加到数组中,所以我们想获取map中的所有键值对,我们只要获取数组中的所有Entry对象,
        //接下来调用Entry对象中的getkey()和getvalue()方法就能获取键值对。
        Set<Entry<String,String>> entrySet=hashmap.entrySet();
        for(Entry<String, String> entry:entrySet){
            System.out.println(entry.getKey()+"-->"+entry.getValue());
        }

        /*
         * hashmap调用默认构造方法会产生一个底层长度为16的Entry数组 
         * int hash=hash(key.hashCode());
         * 首先调用key的hashCode方法来得到一个整数(哈希码),把哈希码作为参数传到hash函数中来进行运算(散列运算)
         * 得到一个整型(散列值)。hashCode()方法能够提高哈希表的性能。
         * int i=indexFor(hash,table.length);
         * 把散列值和数组的长度来进行运算,最终得到entry对象存放到数组的位置(下标)。
         * 
         * hashmap内部的结构是一个数组链表结构。因为不同的key有可能算出来是相同的散列值,根据散列值计算出存放数组
         * 的下标会冲突。
         */
    }
}

3、哈希码的产生和使用

(1)hashCode的常规协定:在Java应用程序执行期间,在对同一对象多次调用hashCode方法时,必须一致地返回相同的整数,前提是将对象进行equals比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。

(2)如果根据equals(Object)方法,两个对象是相等的那么这两个对象中的每个对象调用hashCode方法都必须生成相同的整数结果。(这里的equals方法是指Object类中没有被子类重写过的equals方法)。

(3)如果根据equals(java.lang.Object)方法,两个对象不相等,那么对这两个对象中的任一对象上调用hashCode方法不要求一定生成不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。

(4)举例

package com.iotech.map;

import java.util.HashMap;
import java.util.Map;

public class HashCodeDemo2 {

    public static void main(String[] args) {
        Map<Student, String> map=new HashMap<Student, String>();
        map.put(new Student("jay", 20), "张三");
        map.put(new Student("lisi", 30), "李四");
        map.put(new Student("rose", 20), "玫瑰");
        map.put(new Student("lisi", 30), "陈豪");

        /*要满足student键值相等,首先需要hashCode相等,另外还需要key的equals方法相等。所以需要重写
        hashCode()和equals()方法。*/
        System.out.println(map);
        System.out.println(map.size());

    }

}

class Student{
    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public Student(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Student other = (Student) obj;
        if (age != other.age)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
}

4、TreeMap及常用API

(1)TreeMap类通过使用红黑树实现Map接口。

(2)TreeMap提供按排序顺序存储键/值对的有效手段,同时允许快速检索。

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

(4)TreeMap的构造方法:TreeMap();TreeMap(Comparator comp);TreeMap(Map m);TreeMap(SortedMap sm)。

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

(6)举例一

package com.iotech.map;

import java.util.Set;
import java.util.TreeMap;
import java.util.Map.Entry;

public class TreeMapDemo1 {

    public static void main(String[] args) {
        TreeMap<String,String> treeMap=new TreeMap<String, String>();
        treeMap.put("jack", "zhangsan");
        treeMap.put("mary", "xiaohong");
        treeMap.put("rose", "xiaozhang");
        treeMap.put("free", "xiaoming");
        treeMap.put("rose", "chenhao");

        //TreeMap是按照键来进行排序的,而String实现了comparable接口,所以是没有问题的。
        System.out.println(treeMap);

        Set<Entry<String, String>> entrySet=treeMap.entrySet();
        for(Entry<String, String> entry:entrySet){
            System.out.println(entry.getKey()+"-->"+entry.getValue());
        }
    }
}

(7)举例二

package com.iotech.map;

import java.util.Comparator;
import java.util.TreeMap;

public class TreeMapDemo2 {

    public static void main(String[] args) {
        TreeMap<Person,String> treeMap=new TreeMap<Person,String>(new Comparator<Person>() {

            @Override
            public int compare(Person o1, Person o2) {
                int x=o1.getAge()-o2.getAge();
                if(x>0) return 1;
                else if(x<0) return -1;
                return 0;
            }
        });
        treeMap.put(new Person("zhangsan", 30), "张三");
        treeMap.put(new Person("lisi", 31), "李四");
        treeMap.put(new Person("rose", 32), "玫瑰");
        treeMap.put(new Person("zhangsan", 33), "张三");

        //这里的Person作为TreeMap中的键,也是需要进行排序的,所以Person类也需要继承Comparable接口,
        //因此也需要重写CompareTo()方法。
        System.out.println(treeMap);

    }

}

class Person/* implements Comparable<Person>*/{
    private String name;
    private int age;

    public Person(String name, int age) {
        super();
        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 int compareTo(Person o) {
        int x=this.age-o.getAge();
        if(x>0){
            return 1;
        }else if(x<0){
            return -1;
        }
        return 0;
    }*/
}

5、Comparator和Comparable接口

TreeMap的key存储引用数据类型,需要满足一定条件,要么引用类型实现Comparable接口,要么为该TreeMap容器提供实现Comparator接口的比较器对象。对应上面例二的两种实现方式。

6、案例讲解

(1)给定一个字符数组,数组中内容有重复,现要求打印输出各个字符串出现的次数。

(2)使用HashMap来设计,HashMap的key可以用来保存字符串,value用来保存字符串所对应的次数,从HashMap中获取不存在的key所对应的值为null。

(3)代码实现

package com.iotech.map;

import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Set;

public class HashMapTest {

    public static void main(String[] args) {
        String[] string={"zhangsan","lisi","wangwu","jack","zhangsan","zhangsan","wangwu"};
        HashMap<String,Integer> hashMap=new HashMap<String,Integer>();
        for(int i=0;i<string.length;i++){
            if(null==hashMap.get(string[i])){
                hashMap.put(string[i], 1);
            }else{
                hashMap.put(string[i], hashMap.get(string[i])+1);
            }
        }
        Set<Entry<String,Integer>> entrySet=hashMap.entrySet();
        for(Entry<String,Integer> entry:entrySet){
            System.out.println(entry.getKey()+"-->"+entry.getValue());
        }
    }
}
🐶 您的支持将鼓励我继续创作 🐶
-------------本文结束感谢您的阅读-------------