欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

夯实基础——java基础学习(Map集合)

程序员文章站 2022-10-03 14:37:06
Map集合Map是一种用于保存具有映射结构的数据的数据结构,其基本元素是键对值。因此Map当中保存着两组值,一组值用于保存Map当中的key,另外一种值用于保存Map里的value。key和value都可以是任意的引用数据类型,但是,Map当中的key不允许重复。key和value总是存在着单向的一对一关系,即通过指定的key,总能找到对应的value。如果把map的两组值拆开来看,Map当中的key集合又组成了一个set集合(所有的key没有顺序,key之间不重复),实际上Map当中确实包含了一个ke...

Map集合

Map是一种用于保存具有映射结构的数据的数据结构,其基本元素是键对值。因此Map当中保存着两组值,一组值用于保存Map当中的key,另外一种值用于保存Map里的value。key和value都可以是任意的引用数据类型,但是,Map当中的key不允许重复。

key和value总是存在着单向的一对一关系,即通过指定的key,总能找到对应的value。如果把map的两组值拆开来看,Map当中的key集合又组成了一个set集合(所有的key没有顺序,key之间不重复),实际上Map当中确实包含了一个keySet()方法,用于返回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当中如果需要取出元素,则需要提供元素对应key值。因此,Map有时被称为字典,或者关联数组。

import java.util.HashMap;


public class App {
    public static void main(String[] args) throws Exception {
        HashMap map = new HashMap<>();
        map.put("开始学习java", 109);
        map.put("继续学习java", 19);
        map.put("放弃学习java", 1);
        System.out.println(map);
        map.put("开始学习java", 11);
        System.out.println(map);
    }
}

输出结果如下

{开始学习java=109, 继续学习java=19, 放弃学习java=1}
{开始学习java=11, 继续学习java=19, 放弃学习java=1}

改进的HashMap和Hashtable实现类

HashMap和Hashtable都是Map集合的典型实现类,但是Hashtable与Vector类类似都是一个非常古老的类,,当他出现的时候java还没有Map集合,所以它包含了两个烦琐的方法,即elements()和keys()

Java8改进了HashMap的实现,使用HashMap存在key冲突时依然具有较好的性能。

此外,Hashtable和HashMap存在两点典型区别。

  • Hashtable是一个线程安全的Map实现,但是HashMap是线程不安全的实现,所以HashMap比Hashtable的性能要高一些;但如果有多个线程访问同一个map对象时,使用Hashtable实现类会更好。
  • Hashtable不允许使用null作为key和value,如果试图把null值放入Hashtable中将会引发NullPoniterException异常,但是HashMap可以使用null作为key或者value。

从Hashtable的类名上就可以看出它是一个古老的类,他的命名甚至没有遵守Java的命名规范:每个单词的首字母都应该是大写的。

为了成功的在HashMap和Hashtable当中存储获取对象,用作key的对象必须实现hashCode()方法和equals()方法。

与HashSet结构相同,HashMap也不能保证数据存储的顺序。类似于HashSet、Hashtable和HashMap判断key相同都是使用equals方法,同时保证他们的hashcode也是相同的。

除此之外,HashMap、Hashtable还包含一个containValues方法,用于判断是否包含指定的value方法,而HashMap与Hashtable通过使用equals方法判断两个value是否相等。

与HashSet相同的是如果使用可变的对象作为HashMap的key,并且程序修改了作为key的可变对象,则也可能出现与HashSet相同的情况:程序再也无法准确地访问到Map中被修改过的key。

LinkedHashMap实现类

HashSet有一个LinkedHashSet子类,HashMap也有一个LinkedHashMap子类;LInkedHashMap也使用双向链表来维护key-value对的顺序(其实只需要考虑key的顺序),该链表负责维护Map的迭代顺序,迭代顺序与key-value对的插入顺序保持一致。

LinkedHashMap可以避免对HashMap、Hashtable里的key-value对进行排序(只要插入key-value对是保持顺序即可),同时又可避免使用TreeMap所增加的成本。

LinkedHashMap需要维护元素的插入顺序,因此性能略低于之前的HashMap,但是在遍历整个数据结构当中的元素的时候,他的性能要高于HashMap.

使用Properties读写属性文件

Properties类是Hashtable的子类,正如他的名字所表达的,该对象在处理属性文件的时候特别方便。Properties类可以把Map对象和属性文件关联起来,从而可以把Map对象中的key-value对写入属性文件当中,也可以把属性文件中的“属性名=属性值”加载到Map对象当中。由于属性文件当中的属性名、属性值只能是字符串类型,所以Properties里的key、value都是字符串类型。该类提供了三个方法来修改Properties里的key、value值。

Properties相当于一个key和value都是string类型的map

  • String getProperty(String
    key):获取Properties中指定属性名对应的属性值,类似于Map中的get(Object key)方法
  • String getProperty(String key,String defaultValue):该方法于前一个方法基本相似。该方法多一个功能,如果Properties中不存在指定的key时,则该方法指定默认值。
  • Object setProperty(String key,String value):设置属性值,类似于Hashtable的put()方法。
  • void load(InputStream inStream):从属性文件(以输入流表示)中加载key-value对,把加载到的key-value对追加到Properties里。
  • void store(OutputStream out,String comments):将Properties中的key-value对输出到指定的文件当中。

上面两个方法使用了InputStream类和OutputStream类,它们是java IO体系中的两个基类,关于这两个类的详细介绍,将会之后博客体现

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


public class App {
    public static void main(String[] args) throws Exception {
        Properties properties =new Properties();
        properties.setProperty("username", "eight");
        properties.setProperty("password", "123");
        properties.store(new FileOutputStream("account.ini"), "write");
        Properties properties2 = new Properties();
        properties2.setProperty("gender", "male");
        properties2.load(new FileInputStream("account.ini"));
        System.out.println(properties2);
        
    }
}

输出结果如下:

{password=123, gender=male, username=eight}

上面程序还会生成一个文件,文件内容如下

#write
#Mon Jul 27 21:10:10 CST 2020
password=123
username=eight

Properties可以把key-value对以XML文件的形式保存起来,也可以从XML文件中加载key-value对。

SortedMap接口和TreeMap实现类

正如Set接口派生出的SortedSet子接口,SortedSet接口也有一个TreeSet实现类一样,Map接口所派生出来的SortedMap接口同样有一个TreeMap实现类。

TreeMap就是一个红黑树的数据结构,每个key-value对即作为红黑树的一个节点。TreeMap可以保证所有的key-value对处于有序状态。TreeMap也有两种排序方式。

  • 自然排序:TreeMap的所有key必须实现Comparable接口,而且所有的key应该是同一个类的对象,否则将会抛出一个ClassCastException异常。
  • 定制排序:创建TreeMap时,传入一个Comparator对象。该对象负责对TreeMap中的所有key进行排序。采用定制排序的时不要求Map的key实现Comparable接口。

类似于TreeSet中判断两个元素相等的条件,TreeMap判断两个key相等的标准是:两个key通过compareTo()方法返回为0,就认为这两个key是相等的。

如果使用自定类作为TreeMap的key,且想让TreeMap良好的工作,则重写该类的equals()方法和CompareTo()方法比较应该返回0,如果equals()方法与compareTo()方法的返回结果并不一致,TreeMap与Map的接口规则就会产生冲突。

与TreeSet类似的是,TreeMap中也提供了一系列根据key顺序访问key-value对的方法。

import java.util.TreeMap;

public class App {
    public static void main(String[] args) throws Exception {
        TreeMap treeMap =new TreeMap<>();
        treeMap.put(new R(3), "eight3");
        treeMap.put(new R(4), "eight4");
        treeMap.put(new R(2), "eight2");
        System.out.println(treeMap);
        System.out.println(treeMap.firstEntry());
        System.out.println(treeMap.lastKey());
        System.out.println(treeMap.higherKey(new R(2.5)));
        System.out.println(treeMap.lowerKey(new R(3.5)));
        System.out.println(treeMap.subMap(new R(2.5), new R(3.5)));
    }
}

class R implements Comparable{
    double value ;
    @Override
    public String toString(){
        return this.value+"";
    }
    public R(double value){
        this.value=value;
    }
    public boolean equals(Object object){
        if(this == object){
            return true;
        }
        else if(object!=null&&object.getClass()==R.class){
            R r = (R)object;
            return r.value == this.value;
        }
        return false;
    }
	@Override
	public int compareTo(Object o) {
        // TODO Auto-generated method stub
        R r= (R) o;
        if(this.value> r.value) return 1;
        else if(this.value< r.value)return -1;
        else return 0;
	}
}

输出结果如下

{2.0=eight2, 3.0=eight3, 4.0=eight4}
2.0=eight2
4.0
3.0
3.0
{3.0=eight3}

WeakHashMap实现类

WeakHashMap实现类与HashMap的用法基本相似。与HashMap的区别在于,HashMap的key保留了对象的强引用,这意味着只要该HashMap对象不被销毁,该HashMap的所有key所引用的对象就不会被垃圾回收。HashMap也不会自动删除这些key所对应的key-value对;但WeakHashMap实现类的key只保留了对象的弱引用,这意味着如果WeakHashMap对象的key所引用的对象没有被其他强引用变量所引用,则这些key所引用对象可能被垃圾回收,WeakHashMap也可能自动删除这些key所对应的key-value对。

WeakHashMap中的每个key对象只持有对实际对象的弱引用,因此,当垃圾回收了该key所对应的实际对象的之后,WeakMap会自动删除该key所对应的key-value对。

import java.util.WeakHashMap;

public class App {
    public static void main(String[] args) throws Exception {
        WeakHashMap weakHashMap = new WeakHashMap<>();
        weakHashMap.put(new String("语文"), new Integer("67"));
        weakHashMap.put(new String("数学"), new Integer("68"));
        weakHashMap.put(new String("英语"), new Integer("69"));
        weakHashMap.put("综合", 56);
        System.out.println(weakHashMap);
        System.gc();
        System.out.println(weakHashMap);
    }
}

输出结果如下

{数学=68, 综合=56, 英语=69, 语文=67}
{综合=56}

从上面运行结果可以看出,当系统进行垃圾回收的时候,删除了weakHashMap对象前三个的key-value对,这是因为添加的三个key-value对时,这三个key都是匿名的字符串对象,WeakHashMap只保留了对他们的弱引用,这样垃圾回收就会删除它们三个。而第四个key-value是一个字符串直接量,所以系统并不会回收它。

identityHashMap实现类

identityHashMap实现类与HashMap实现类基本相同,只是在判断两个key相等的条件有些不同,HashMap判断两个key相等需要满足两个条件(1.equals方法返回true。2.hashCode相等。)而identityHashMap则是严格的要求key1==key2。

import java.util.IdentityHashMap;

public class App {
    public static void main(String[] args) throws Exception {
        IdentityHashMap identityHashMap = new IdentityHashMap();
        identityHashMap.put(new String("语文"), 67);
        identityHashMap.put(new String("语文"), 68);
        String java ="java";
        identityHashMap.put(java,67);
        identityHashMap.put(java,99);
        System.out.println(identityHashMap);
    }
}

输出结果如下

{语文=67, java=99, 语文=68}

EnumMap实现类

EnumMap实现类是一个与枚举类一起使用的Map实现,EnumMap中的所有key都必须是单个枚举类的枚举值。创建EnumMap的时候必须显示或者隐式的制定这枚举类的类型。

  • EnumMap在内部以数组的形式保存,所以这种实现形式十分高效紧凑
  • EnumMap根据key的自然排序来维护key-value对的顺序。
  • EnumMap类不允许使用null作为key,但是允许使用null作为value。

各map实现类的性能分析

对于Map的常用实现类来说,虽然HashMap和Hashtable的实现机制几乎一样,但由于Hashtable是一个古老的、线程安全的集合,因此HashMap通常比Hashtable要快。

TreeMap通常要比HashMap、Hashtable要慢(尤其在插入删除节点的时候更慢),因为TreeMap底层采用红黑树来管理key-value键值对。

使用TreeMap的一个好处就是key总是处于有序状态。

对于一般的应用场景来说应当尽量考虑使用HashMap。LinkedHashMap要比HashMap要慢一些,因为需要维护一个链表。EnumMap性能最好,但是只能保存指定的枚举类的key。

本文地址:https://blog.csdn.net/weixin_40787604/article/details/107450730