Java笔记----Map接口(非源码向)

初学者,先看结论,再用源码去印证结论,更高效。冲冲冲!!!

一、Map接口及其实现类特点:

Map:双列数据,存储key-value对的数据 —类似于高中的函数: y = f(x)

  • HashMap:作为Map的主要实现类;线程不安全的,效率高;存储null的key和value
    • LInkedHashMap:继承自HashMap。保证在遍历的元素的时候,可以按照添加的顺序实现遍历,因为在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素,对于频繁的遍历操作,此类执行效率高于HashMap
  • TreeMap:保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序,底层使用红黑树。
  • Hashtable:作为古老的实现类:线程安全的,效率低;不能存储null的key和value。
    • Properties:常用来处理配置文件。key和value都是String类型

二、Map结构的理解:

  • Map中的key:无序的、不可重复的, 使用Set存储所有的key —>key所在的类重写equals()和hashCode()
  • Map中的value:无序的、可重复的,使用Collection存储所有的value
  • 一个键值对: key-value 构成了一个Entry对象。
  • Map中的entry:无序的、不可重复的,使用Set存储所有的entry

三、HashMap源码分析结论:jdk 7为例

      HashMap map = new HashMap():
在实例化以后,底层创建了长度是16的一维数组Entry[] table。
       ...可能已经执行过多次put之后调用put()...
       map.put(key1, value1):
       
  1.首先,调用key1所在类的hashCode()参与哈希运算hash(),计算key1哈希值,此哈希值经过某种
  		算法计算以后,得到在Entry数组中的存放位置。
     2.如果此位置上的数据为空,此时的key1-value1添加成功。----情况1
     3.如果此位置上的数据不为空,(意味者此位置.上存在一个或多个数据(以链表形式存在)),比较
       		key1和已经存在的一个或多个数据的哈希值:
       4.如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功--情况2
       5.如果key1的哈希值和已经存在的某一一个数据(key2-value2) 的哈希值相同,继续比较:
           		调用key1所在类的equals(key2)
               6.如果equals()返回false:此时key1-value1添加成功。----情况3
               7.如果equals()返回true:使用value1替换value2。
  补充:关于情况2和情况3:此时key1-value1 和原来的数据以链表的方式存储。
  
  在不断地添加过程中,会涉及到扩容问题:当超出临界值12并且添加下一个元素计算出来的位置还不空,	   
  将要形成链表时,则扩容为原来容量的2倍,并将数据copy过来,重新计算哈希值并填充底层数组,可
  能会将原来的链表打散.

JDK 8中的底层实现原理
1. new HashMap(),底层并没有创建一个长度为16的数组
2.底层数组为Node[]而非Entry[]
3.首次调用put()时,才将数组创建出来
4.jdk7底层结构只有:数组+链表。jdk8 中底层结构:数组+链表+红黑树。
	当HashMap中的其中-一个链的对象个数如果达到了8个,此时如果capacity没有
	达到64,那么HashMap会先扩容解决,如果已经达到了64,那么这个链会变成
	树,结点类型由Node变成TreeNode类型。此时此索引位置上的所有数据改为使用红黑树存储,
	查找效率更高,虽然用HashCode定位很方便,但到了链表的时候还得挨个对比,红黑树是有序的,
	左小右大,每次对比都能少一半,效率更高。当然,如果当映射关系被移除后,下次resize方法
	时判断树的结点个数低于6个,也会把树再转为链表。

源码中变量含义:

 *  DEFAULT_ INITIAL_ CAPACITY : HashMap的默认容量,16
 *  DEFAULT_ LOAD_ FACTOR: HashMap 的默认加载因子: 0.75,提前扩容,兼顾数组利用率
 * 		和减少树形结构
 *  threshold:扩容的临界值,=容量*加载因子: 16 * 0.75 => 12
 *  TREEIFY_ THRESHOLD: Bucket中链表长度大于该默认值,就要考虑是否转化为红黑树:8
 *  MIN_ TREEIFY_ CAPACITY: 桶中的Node被树化时最小的hash表容量: 64

四、LinkedHashMap

static cLass Entry<K, V> extends HashMap.Node<K,V> {
           Entry<K,V> before, after;// 能够记录添加的元素的先后顺序
           Entry(int hash, K key, V value, Node<K, V> next) {
           super(hash, key, value, next);
       }
  }

五、Map中的方法

  • 添加、删除、修改操作:
    • object put (Object key, object value): 将指定key-vaLue添加到(或修改)当前map对象中
    • void putALL(Map m) :将m中的所有key-value对存放到当前map中
    • object remove(0bject key): 移除指定key的key-value对, 并返回value
    • void clear(): 清空当前map中的所有数据
  • 元素查询的操作: .
    • object get(0bject key): 获取指定key对应的value
    • boolean containsKey(Object key):是否包含指定的key
    • boolean containsValue(0bject value): 是否包含指定的value
    • int size():返回map中key-value对的个数
    • boolean isEmpty(): 判断当前map是否为空
    • boolean equals(Object obj):判断当前map和参数对象obj是否相等
  • 原视图的操作方法:
    • Set keySet(): 返回所有key构成的Set集合
    • Collection values():返回所有value构成的Collection集合
    • Set entrySet(): 返回所有key-value对构成的Set集合

六、Map示例代码

遍历:

  • key:map.keySet(),用Set接收。
  • values:map.values(),用Collection接收。
  • key-values:
    • 方式一:map.entrySet(),遍历entrySet,通过entry.getKey(),entry.getValue()取值
    • 方式二:map.ketSet(),遍历key的同时,通过key找到value–>map.get(key)
@Test
    public void test1(){
        Map map = new HashMap();
//        map = new Hashtable();//这个会导致报错,NullPointerException
        map.put(null, null);
    }

    @Test
    public void test2(){
        Map map = new HashMap();
        map.put("aa", 123);
        map.put("bb", 1234);
        map.put("45", 78);

        //遍历所有的key集合:keySet()
        Set set = map.keySet();
        Iterator iterator1 = set.iterator();
        while(iterator1.hasNext()){
            System.out.println(iterator1.next());
        }

        //遍历所有的value集合:values()
        Collection values = map.values();
        for (Object obj : values){
            System.out.println(obj);
        }

        //遍历所有的key-value
        //方式一:entrySet()
        Set entrySet = map.entrySet();
        Iterator iterator2 =entrySet.iterator();
        while(iterator2.hasNext()){
            Object obj = iterator2.next();
            //entrySet集合中的元素都是entry,这个内部接口还定义了两个方法
            Map.Entry entry = (Map.Entry) obj;
            System.out.println(entry.getKey() + "->" + entry.getValue());
        }

        //方式二:组合keySet()和get()
        Set keySet = map.keySet();
        Iterator iterator3 = keySet.iterator();
        while(iterator3.hasNext()){
            Object key = iterator3.next();
            Object value = map.get(key);
            System.out.println(key+"-->"+value);
        }

    }

七、TreeMap

直接看代码

    /*
    向TreeSet中添加key-value,要求key必须是由同一个类创建的对象
    因为要进行排序:自然排序、定制排序
     */
    @Test
    public void test(){
        java.util.TreeMap map = new java.util.TreeMap();
        User u1 = new User("Tom",12);
        User u2 = new User("Mary",22);
        User u3 = new User("Bob",32);
        User u4 = new User("Kite",42);
        map.put(u1, 98);
        map.put(u2, 89);
        map.put(u3, 79);
        map.put(u4, 69);

        Set entrySet = map.entrySet();
        Iterator iterator2 =entrySet.iterator();
        while(iterator2.hasNext()){
            Object obj = iterator2.next();
            //entrySet集合中的元素都是entry,这个内部接口还定义了两个方法
            java.util.Map.Entry entry = (java.util.Map.Entry) obj;
            System.out.println(entry.getKey() + "->" + entry.getValue());
        }
    }

    @Test
    public void test2(){
        java.util.TreeMap map = new java.util.TreeMap(new Comparator() {
            @Override
            public int compare(Object o, Object t1) {
                if(o instanceof User && t1 instanceof User){
                    User u1 = (User)o;
                    User u2 = (User)t1;
                    return Integer.compare(u1.getAge(), u2.getAge());
                }
                throw new RuntimeException("输入类型不匹配");
            }
        });
        User u1 = new User("Tom",12);
        User u2 = new User("Mary",22);
        User u3 = new User("Bob",32);
        User u4 = new User("Kite",42);
        map.put(u1, 98);
        map.put(u2, 89);
        map.put(u3, 79);
        map.put(u4, 69);

        Set entrySet = map.entrySet();
        Iterator iterator2 =entrySet.iterator();
        while(iterator2.hasNext()){
            Object obj = iterator2.next();
            //entrySet集合中的元素都是entry,这个内部接口还定义了两个方法
            java.util.Map.Entry entry = (java.util.Map.Entry) obj;
            System.out.println(entry.getKey() + "->" + entry.getValue());
        }
    }

User定义如下:

import java.util.Objects;

public class User implements Comparable{
    private String name;
    private int age;

    public User() {
    }

    public User(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 String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        //为了证明调过equals
        System.out.println("User.equals...");
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return age == user.age &&
                Objects.equals(name, user.name);
    }

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

    @Override
    public int compareTo(Object o) {
        if(o instanceof User){
            User user = (User)o;
            //return this.name.compareTo(user.name);
            int compare = this.name.compareTo(user.name);
            if(compare != 0){
                return compare;
            }else {
                return Integer.compare(this.age, user.age);//姓名一样
            }
        }else {
            throw new RuntimeException("输入的类型不匹配");
        }
    }
}

八、Collections:操作Collection、Map的工具类

基本方法:

  • reverse(List):反转List中元素的顺序

  • shuffle(List):对List集合元素进行随机排序

  • sort(List):根据元素的自然顺序对指定List集合元素按升序排序

  • sort(List, Comparator): 根据指定的Comparator 产生的顺序对List集合元素进行排序

  • swap(List, int, int): 将指定list集合中的i处元素和j处元素进行交换

  • object max(Collection): 根据元素的自然顺序,返回给定集合中的最大元素

  • object max((Collection, Comparator): 根据Comparator 指定的顺序,返回给定集合中的最大元素

  • object min(Collection)

  • object min(Collection, Comparator)

  • int frequency(Collection, object): 返回指定集合中指定元素的出现次数

  • void copy(List dest,List src):将src中的内容复制到dest中

  • boolean replaceAll(List list, object oldVal, object newVal): 使用新值替换List对象

    @Test
    public void test2(){
        List list = new ArrayList();
        list.add(123);
        list.add(456);
        list.add(789);
        list.add(987);
        list.add(654);

        //报异常 IndexOutOfBoundsException: Source does not fit in dest
//        List destination = new ArrayList();
//        Collections.copy(destination, list);
		//得用一个数组先把它撑起来
        List destination = Arrays.asList(new Object[list.size()]);
        Collections.copy(destination, list);
        System.out.println(destination);

        /*
        Collections类中提供了多个synchronizedXxx()方法,该方法可使将指定集
        合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题
         */
        //返回的list1就是线程安全的
        Collection list1 = Collections.synchronizedCollection(list);
    }
©️2020 CSDN 皮肤主题: 1024 设计师:上身试试 返回首页