Map学习 java

Map

Map用于保存具有映射关系的数据,因此Map集合中保存着两组值。一组保存Map里的key,另外一组保存Map里的value,key和value都是可以任何引用类型的数据。Map中的key不允许重复,即同一个Map对象的任何两个key通过equals方法比较总能返回false。
key和value之间存在单向一对一关系,即通过指定的key总能找到唯一的,确定的value。从map中取出数据时,只要给出指定的key就可以取出对应的value。如果把map里面的两组值拆开来看,map里所有的key放在一起来看,它们就组成了一个set集合,map里面有一个keySet()方法,用于返回Map里所有key组成的Set值。
不仅如此Map里的key集和set集合里元素存储形式也很像,Map子类和Set子类在名字上也惊人地相似。比如Set接口下有HashSet、LinkedHashSet SortedSet(接口) TreeSet EnumSet等子接口和实现类。而Map接口下则有HashMap LinkedHashMap SortedMap(接口) TreeMap EnumMap 等子接口和实现类。正如他们的名字所暗示的,Map的这些实现类和子接口中key集的存储形式和对应Set集合中元素的存储形式完全相同。
Set与Map之间的关系非常密切,虽然Map中放的元素是key-value对,Set集合中放的元素是单个元素,但如果把key-value对中的value当成key的附庸:key在哪里,value就跟在哪里。这样就可以像对待Set一样来对待Map了。事实上,map提供了一个Entry内部类来封装key-value对,而计算Entry存储时,只考虑了Entry封装的key。从java源码来看,java是先实现了Map,然后通过包装一个所有value都为空对象的Map就实现了Set集合。
如果把Map所有value放在一起看,它们又非常类似于一个List:元素与元素之间可以重复,每个元素可以根据索引来查找,只是Map中索引不再使用整数值,而是以另一个对象作为索引。如果需要从List集合中取出元素,则需要提供该元素的数字作为索引:如果需要从Map中取出元素,则需要提供该元素的key索引。因此,Map有时候也被称为字典。p324中提供Map接口中一些常用的方法。HashMap重写了toStringg()方法,实际上所有的Map实现类都实现了toString()方法,调用Map对象的toString()方法总是返回如下格式的字符串:{key1=value1,key2=value2…}。
简单测试如下:

package Map;

import java.util.HashMap;

public class MapTest {
    public static void main(String[] args) {
        HashMap<String, Integer> map = new HashMap<>();
        map.put("test1",109);
        map.put("test2",10);
        map.put("test3",79);
        map.put("test4",79);
        //如果放入重复的key,新的value会覆盖原有的value,并返回被覆盖的value
        System.out.println(map.put("test2",222));
        System.out.println(map);
        //判断是否包含指定的value
        System.out.println(map.containsValue(222));
        //判断是否包含指定的key
        System.out.println(map.containsKey("test2"));
        //获取所有的key然后进行遍历
        for (String key: map.keySet()
             ) {
            System.out.println(key + "-->" + map.get(key));
        }
        //通过key来删除key value
        map.remove("test2");
        System.out.println(map);
    }
}

结果如下:
在这里插入图片描述

在java8中为Map增加了一些方法,具体方法在p327中有讲,现在先进行简单的测试一下方法的使用。

package Map;

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

public class MapTest2 {
    public static void main(String[] args) {
        Map hashMap = new HashMap();
        hashMap.put("xiaolu",20);
        hashMap.put("xiaohuan",30);
        hashMap.put("xiaoli",40);
        hashMap.put("xiaoding",50);
        //尝试替换key为"xiaoji"的value,由于原map中没有对应的key,所以map不会改变.
        hashMap.replace("xiaoji",60);
        System.out.println(hashMap);
        //使用原value与传入参数计算出来的结果覆盖原有的value
        hashMap.merge("xiaoding",10,(oldValue,param)->(Integer) oldValue + (Integer) param);
        //可以看到xiaoding所对应的数据增加了10
        System.out.println(hashMap);
        //当key为xiaohu对应的value为null或不存在时,使用计算结果作为新的value
        hashMap.computeIfAbsent("xiaohu",key-> ((String) key).length());
        System.out.println(hashMap);
        //当key为“xiaohu”对应的value存在时,使用计算的结果作为新的value
        hashMap.computeIfPresent("xiaohu",(key,value)->(Integer)value*(Integer)value);
        System.out.println(hashMap);
    }
}

结果如下:
在这里插入图片描述

HashMap和Hashtable都是Map接口的典型实现类,他们之间的关系就类似于ArrayList和Vector。HashMap是线程不安全的,Hashtable是线程安全的。Hashtable中value和key都不允许为null。HashMap就可以,但是,key只能存在一个null,value则可以无数个null。Hashtable比较古老,一般不用。
与HashSet集合不能保证元素顺序一样,HashMap也不能保证key-value的顺序。类似于HashSet,HashMap判断两个key相等的标准也是:两个key通过equals()方法返回true,两个key的hashCode值也相等。
除此之外,HashMap还有一个containsValue()方法,用于判断是否包含指定的value。
HashMap如何判断两个value相等呢?只要判断两个对象通过equals()方法比较返回true即可。
代码如下:

package Map;


import java.util.HashMap;


    //A这个类,他的equals()和hashCode()这两个方法都是通过其实例变量决定
    class A{
        private int count;
        public A(int count){
            this.count = count;
        }
        public boolean equals(Object object){
            //说明同一个对象
            if(object == this){
                return true;
            }else if(object!=null && object.getClass() == this.getClass()){
                A a = (A)object;
                return a.count == this.count;
            }else {
                return false;
            }
        }

        /**
         * 通过count来返回它的hashCode值
         * @return
         */
        public int hashCode(){
           return this.count;
        }
    }
    class B{
        public boolean equals(Object object){
            return true;
        }
    }
public class MapTest3 {
    public static void main(String[] args) {
        A a = new A(2000);
        A a1 = new A(122);
        A a2 = new A(11);
        B b = new B();
        HashMap hashMap = new HashMap();
        hashMap.put(a,"大话数据结构");
        hashMap.put(a1,"大话设计模式");
        hashMap.put(a2,"网络是怎么连接的");
        System.out.println(hashMap);
        //b调用equals()方法返回true
        System.out.println(hashMap.containsValue(b));
        //两个对象的equals()相等,并且hashCode()相等
        System.out.println(hashMap.containsKey(new A(2000)));
        //删除最后一个键值对
        hashMap.remove(a2);
        System.out.println(hashMap);
    }
}

与hashMap一样,尽量不要使用可变对象做为HashMap、Hashtable的key,如果确实需要使用可变对象作为key,则尽量不要在程序中修改做为key的可变对象。
当使用自定义类作为HashMap、Hashtable的key时,如果重写改类的equals(Object obj)和hashCode()方法时,则应该保证两个方法的判断标准一致。
LinkedHashMap实现类
1.它是HashMap的子类;
2.它使用双向链接来维护key-value对顺序,迭代顺序与key-value对的插入顺序保持一致;
3.它适合遍历全部元素;
4.HashMap适合添加元素;
测试代码:

package Map;

import java.util.LinkedHashMap;

public class LinkedHashMapTest {
    public static void main(String[] args) {
        LinkedHashMap linkedHashMap = new LinkedHashMap();
        linkedHashMap.put(1,1);
        linkedHashMap.put(2,1);
        linkedHashMap.put(3,1);
        System.out.println(linkedHashMap);
        //用forEach来进行遍历,可以选择遍历的格式
        linkedHashMap.forEach((key,value)-> System.out.println("key = " + key + " value = "  + value));
    }
}

结果如下:
在这里插入图片描述

使用Properties类来读写属性文件
Properties类在处理属性文件时特别方便。Properties可以把Map对象和属性文件关联起来,从而把Map对象中的key-value对写入属性文件中,也可以把属性文件中的“属性名=属性值”加载到Map对象中。由于属性文件属性名和属性值只能是字符串类型,故Properties中key和value都是字符串类型。

package Map;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;

public class PropertiesTest {
    public static void main(String[] args) throws IOException {
        Properties properties = new Properties();
        properties.setProperty("username","xiaolu");
        properties.setProperty("password","123456");
        //将Properties中的key-value对保存到a.txt文件中
        properties.store(new FileOutputStream("a.txt"),"这是备注");

        Properties properties2 = new  Properties();
        properties2.setProperty("gender","male");
        //将a.txt文件中key-value追加到properties2中
        properties2.load(new FileInputStream("a.txt"));
        System.out.println(properties2);
    }
}

在这里插入图片描述
所以,Properties可以把key-value以XML文件的形式进行保存,也可以从XML文件中加载key-value对,用法与此类似。
TreeMap
1.TreeMap是一个红黑树的数据结构
2.TreeMap存储时,根据key对节点进行排序,TreeMap可以保证所有的key-value对处于有序状态;
3.TreeMap有两种排序方式;
4.自然排序:TreeMap的所有key必须实现Comparable
5.定制排序:创建TreeMap时,传入一个
简单来说:TreeMap的作用是当你要存key-value时;同时,你想要按照顺序进行存储,这样就可以使用它来进行了。
代码如下:

package Map;

import java.util.TreeMap;

class R implements Comparable {
    int count;

    public R(int count) {
        this.count = count;
    }

    public boolean equals(Object object) {
        if (object == this) {
            return true;
        }
        if (object != null && object.getClass() == R.class) {
            R obj = (R) object;
            return obj.count == this.count;
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {
        return this.count;
    }

    @Override
    public String toString() {
        return "R{" +
                "count=" + count +
                '}';
    }

    @Override
    public int compareTo(Object o) {
        R r = (R) o;
        if (r.count > this.count) {
            return -1;
        } else if (r.count < this.count) {
            return 1;
        }else {
            return 0;
        }
    }
}

public class TreeMapTest {
    public static void main(String[] args) {
        TreeMap treeMap = new TreeMap();
        treeMap.put(new R(3), "lu");
        treeMap.put(new R(-5), "luguo");
        treeMap.put(new R(9), "luguobao");
        System.out.println(treeMap);
        //返回最大的key所对应的key-value
        System.out.println(treeMap.lastEntry());
        //返回位于key后面的一位key-value值
        System.out.println(treeMap.higherKey(new R(2)));
        System.out.println(treeMap.lowerEntry(new R(2)));
    }
}

WeakHashMap
WeakHashMap和HashMap的使用方法基本一致,但是,WeakHashMap是弱引用,而HashMap是强引用。对应HashMap来说,只要HashMap对象不被销毁,则该对象所有的key都不会被垃圾回收。可以理解为HashMap对其有着强引用。但是,如果是选择WeakHashMap,则key只是保持着弱引用,也就是说,当key没有被其他强引用对象所引用的话 就会被垃圾回收站给回收。

package Map;

import java.util.WeakHashMap;

public class WeakHashMapTest {
    public static void main(String[] args) {
        WeakHashMap weakHashMap = new WeakHashMap();
        weakHashMap.put(new String("yuwen"), "xixi");
        weakHashMap.put(new String("shuxue"), "xixi");
        weakHashMap.put(new String("yingyu"), "xixi");
        weakHashMap.put("zhongguo","nihao");
        System.out.println(weakHashMap);
        //通知系统进行垃圾回收
        System.gc();
        System.runFinalization();
        System.out.println(weakHashMap);
    }
}

因为前三个new String 是匿名对象,没有强引用,当执行垃圾回收以后,会发现第二次输出的只有一个key-value。“zhongguo”:这个代表的是字符串直接量 使用缓冲池保存对该字符串对象的强引用。
如果要使用WeakHashMap则加入key应该是弱引用的,不然,就没有意义了。
EnumMap
注意点:
1.内部以数组形式进行保存,非常高效
2.所有的key必须是单个枚举类的枚举值,创建时需要和枚举类进行联系
3.不允许使用null为key值
4.排序是按照Season的排序进行的
用法:

package Map;

import java.util.EnumMap;

enum  Season{
    SPRING,SUMMER,FALL,WINTER
}
public class EnumMapTest {
    public static void main(String[] args) {
        EnumMap enumMap = new EnumMap(Season.class);
        enumMap.put(Season.SUMMER,"夏天到了");
        enumMap.put(Season.SPRING,"秋天到了");
        System.out.println(enumMap);
    }
}

结果如下:
在这里插入图片描述
各Map实现类的分析
HashMap:一般情况多使用它,因为HashMap底层是采用数组来存储key-value的,为了快速查询设计的。
TreeMap:由于底层是用红黑树来进行管理key-value。所以,速度会比HashMap慢很多,但是,它的优点是无须进行排序操作。当TreeMap被填充后,就可以调用keySet(),取得由key组成的Set,然后,使用toArray()方法生成key数组,接下来使用Arrays的binarySearch()方法在已排序的数组中快速查询对象。
在这里插入图片描述
这样就可以知道new R(-3)是在索引为1的位置上。
所以,一般情况下,使用HashMap,如果程序需要一个总是排好序的Map,则使用TreeMap。LinkedHashMap比HashMap慢一些,性能没有出色之处,他使用==而不是equals来判断元素相等。EnumMap的性能最好,但它只能使用同一个枚举类的枚举值来作为key。
关于==和equals()的区别,可以参考以下博客==与equals区别
重点: HashSet和HashMap的性能选项
HashSet和HashMap中,采用hash算法来决定集合中元素的存储位置。hash表中存储元素的位置称为桶,正常来说,一个桶中纯一个元素的时候,效率是最高的。hash算法可以根据hashCode值计算出“桶”的存储位置,在发生冲突的时候,单个桶会存储多个元素,以链表的方式来进行存储。
HashSet、HashMap中hash表有如下属性:
容量:hash表中桶的数量
初始化容量:创建hash表时桶的数量。这个可以指定的
尺寸:当前hash表中记录的数量
负载因子:负载因子等于 “尺寸/容量” 负载因子小的时候,代表表中冲突少、适合插入。
负载极限:决定了hash表的最大填满程度,当到达指定负载极限时,hash表会自动成倍的增加容量,桶的数量。并且将原来的对象重新分配到新桶内
负载极限默认为0.75 这是时间和空间的一种折中:如果负载极限太高,可以减小占用内存空间,但是会增加查询数据的时间开销。如果负载极限过低,就会增加hash表所占用的内存空间。
**使用建议:**如果知道要存很多数据,就在初始化的过程中,使用较大的初始化容量。更高效的增加记录。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值