资讯专栏INFORMATION COLUMN

java集合-Map

Little_XM / 1480人阅读

摘要:增强的集合都可以是任何引用类型的数据,的不允许重复即同一个对象的任何两个通过方法比较总是返回。的这些实现类和子接口中集的存储形式和对应集合中元素的存储形式完全相同。根据的自然顺序,即枚举值的定义顺序,来维护对的顺序。

Java8增强的Map集合

Key-value都可以是任何引用类型的数据,Map的Key不允许重复即同一个Map对象的任何两个key通过equals方法比较总是返回false。

如果把Map里的所有key放在一起来看,他们就组成了一个Set集合:所有key没有顺序,key与key之间不能重复,实际上Map确实包含了一个keySet方法,用来返回Map里所有key组成的Set集合。

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都为null的Map就实现了Set集合。

如果把Map里的所有value放在一起来看,他们又非常类似于一个List:元素与元素之间可以重复,但每个元素可以根据索引来查找,只是Map中的索引不再使用整数值,而是以另一个对象作为索引。

public class MapTest {
    public static void main(String[] args) {
        Map map = new HashMap<>();
        map.put("java", 1);
        map.put("python", 2);
        map.put("hadoop", 3);
        //{python=2, java=1, hadoop=3}
        System.out.println(map);
        //如果当前Map中已经有一个与该Key相等的key-value对,则新的key-value会覆盖原来的key-value对。并返回之前被覆盖的value
        Integer put = map.put("java", 4);
        System.out.println(put);//1
        for(Entry map2:map.entrySet()) {
            System.out.println(map2);
            //python=2
            //java=4
            //hadoop=3
        }
        
        for(Object obj : map.keySet()) {
            System.out.println(obj);//python java hadoop
        }
    }
}
Java8为Map新增加的方法
public class NewMapTest {
    public static void main(String[] args) {
        Map map = new HashMap<>();
        map.put("123", 456);
        //{123=456}
        System.out.println(map);
        
        /**
         * compute 和 computeIfAbsent 和 computeIfPresent总结
         * 
         * compute 如果函数式接口中的返回值不为null,那就是用接口中返回值来覆盖原value
         *                 如果接口中返回值为null,那么就删除此map对
         *                 如果原value为null,那就用接口中返回值覆盖原value
         * computeIfAbsent
         *                 如果函数式接口中的返回值不为null,而且原value不为null,那么不做改变
         *              如果接口中返回值为null,不做改变
         *              如果原value为null,那么就使用接口中的返回值覆盖原value
         */
        /**
         * map.compute(key, remappingFunction)
         * 该方法使用remappingFunction根据原来key-value对计算一个新value
         * 只要新value不为null,就使用新value覆盖原来value;
         * 如果原来value不为null,但新value为null,则删除原key-value对;
         * 如果原value,新value同时为null,那么方法不改变任何key-value对,直接返回null;
         */
        /*
        Integer compute = map.compute("123", (key,value) -> Integer.parseInt(key));
        System.out.println(compute);//123
        System.out.println(map);//{123=123}
        
        Integer compute = map.compute("123", (key,value) -> null);
        System.out.println(map);//{}
         */
        /*
        map.put("1234", null);
        Integer compute = map.compute("1234", (key,value) -> 0);
        System.out.println(compute);//0
        System.out.println(map);//{123=456, 1234=0}
        */
        
        /**
         * Absent 缺席
         *         map.computeIfAbsent(key, mappingFunction)
         * 如果传给该方法的key参数在Map中对应的value为null,则使用mappingFunction根据原来key,value计算一个新的结果
         * 如果计算结果不为null,则用计算结果覆盖原有的value。如果原Map原来不包括该Key,那么该方法可能会添加一组key-value对。
         */
        /*
        map.put("1234", null);
        map.computeIfAbsent("1234", (key) -> Integer.parseInt(key));
        System.out.println(map);//{123=456, 1234=1234}
        */
        /*
        Integer computeIfAbsent = map.computeIfAbsent("1231", (e) -> Integer.parseInt(e));
        System.out.println(computeIfAbsent);//1231
        System.out.println(map);//{123=456, 1231=1231}
        */
        
        /*
        Integer computeIfAbsent = map.computeIfAbsent("123", (e) -> Integer.parseInt("123123123"));
        System.out.println(computeIfAbsent);//456
        System.out.println(map);//{123=456}
        */
        
        
        /**
         * Present 提出;介绍;呈现;赠送
         * map.computeIfPresent(key, remappingFunction)
         * 如果传给该方法的key参数在Map中对应的value不为null,该方法使用remappingFunction根据原key-value计算一个新结果,
         * 如果计算结果不为null,则使用该结果覆盖原来的value,
         * 如果计算结果为null,则删除原key-value对
         */
        /*
        Integer computeIfPresent = map.computeIfPresent("123", (key,value) -> 0);
        System.out.println(computeIfPresent);//0
        System.out.println(map);//{123=0}
        */
        /*
        Integer computeIfPresent = map.computeIfPresent("1234", (Key,value) -> 0);
        System.out.println(computeIfPresent);//null
        System.out.println(map);//{123=456}
        */
        /*
        map.put("1234", null);
        Integer computeIfPresent = map.computeIfPresent("1234", (key,value) -> 12);
        System.out.println(computeIfPresent);//null
        System.out.println(map);//{123=456, 1234=null}
        */
        /*
        Integer computeIfPresent = map.computeIfPresent("123", (key,value) -> null);
        System.out.println(computeIfPresent);//null
        System.out.println(map);//{}
        */
        //获取指定key对应的value,如果key不存在那么就返回指定value
        Integer orDefault = map.getOrDefault("1234", 452);
        System.out.println(orDefault);//452
        
        /**
         * 该方法会先根据key参数获取该Map中对应的value,如果获取的value为null
         * 则直接用传入的value覆盖原有的value,在这种情况下,可能要添加一组map对,
         * 如果value不为null,则使用函数接口根据value,新value计算出一个新的结果,并用得到的结果去覆盖原有的value
         */
        Integer merge = map.merge("123", 123123, (key,value) -> value+1000);
        System.out.println(merge);//124123
        System.out.println(map);//{123=124123}
    }
}
Java8改进的HashMap和Hashtable实现类

Hashtable和HashMap区别

Hashtable是一个线程安全的Map实现,但HashMap是线程不安全的实现,所以HashMap比Hashtable的性能高一点,但如果有多个线程访问同一个Map对象时,使用Hashtable实现类会更好、

Hashtable不允许使用null作为key和value,如果试图把null值放入Hashtable里,将引发空指针异常,但HashMap可是使用null作为key和value、

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

类似于HashSet,HashMap,Hashtable判断两个key相等的标准也是:两个key通过equals方法返回true,两个key的hashCode值也相等。

HashMap和Hashtable判断两个value相等的标准是:只要两个对象通过equals方法比较返回true即可。

public class ABHashtable {
    public static void main(String[] args) {
        Hashtable hashtable = new Hashtable<>();
        hashtable.put(new A(123), "123");
        hashtable.put(new A(1234), "1234");
        hashtable.put(new A(1236), new B());
        System.out.println(hashtable);
        
        System.out.println(hashtable.containsValue("32342342"));//true
        System.out.println(hashtable.containsKey(new A(123)));//true
        System.out.println(hashtable.containsKey(new A(123123)));//false
    }
}    

上述代码解释:上述Hashtable中包含了B对象,而且重写了B对象的equals方法,它与任何对象通过equals都会返回true,所以在第一个输出是true。根据Hashtable判断两个key相等的标准,在第二个输出的时候,因为通过equals和hashCode都返回true,所以Hashtable判断这两个key相等,所以为true。

与HashSet类似的是,如果使用可变对象作为HashMap,Hashtable的key,如果程序修改了可变对象,那么程序再也无法准确访问到Map中被修改过的key。

public class ABHashtable2 {
    public static void main(String[] args) {
        HashMap hashtable = new HashMap();
        hashtable.put(new A(123), "123");
        hashtable.put(new A(1234), "1234");
        //{mapTest.A@4d2=1234, mapTest.A@7b=123}
        System.out.println(hashtable);
        A next = (A) hashtable.keySet().iterator().next();
        System.out.println(next.count);//1234
        next.count = 123;
        //{mapTest.A@7b=1234, mapTest.A@7b=123}
        System.out.println(hashtable);
        
        hashtable.remove(new A(123));
        //只能删除没有被修改的key所对应的key-value对
        System.out.println(hashtable);//{mapTest.A@7b=1234}
        System.out.println(hashtable.get(new A(123)));//null
    }
}

尽量不要使用可变对象作为key,如果确实需要,则尽量不要在程序中修改作为key的对象。

LinkedHashMap实现类

LinkedHashMap也使用双向链表来维护key-value对的次序,其实只需要考虑key的次序,该链表负责维护Map的迭代顺序,迭代顺序与key-value对的插入顺序保持一致。

因为他使用链表来维护内部顺序,所以在迭代访问Map里的全部元素时将有较好的性能,迭代输出LinkedHashMap的元素时,将会安添加key-value的顺序输出。

public class LinkedHashMapS {
    public static void main(String[] args) {
        LinkedHashMap map = new LinkedHashMap<>();
        map.put("1", "1");
        map.put("2", "2");
        map.put("3", "3");
        System.out.println(map);//{1=1, 2=2, 3=3}
    }
}
使用Properties读写属性文件

Properties是Hashtable类的子类

Properties相当于一个key,value都是String的Map

public class PropertiesTest {
    public static void main(String[] args) throws FileNotFoundException, IOException {
        Properties properties = new Properties();
        properties.setProperty("w","zq");
        properties.setProperty("w1","zq1");
        properties.setProperty("w2","zq2");
        //输出文件目录,文件说明
        properties.store(new FileOutputStream(new File("myProperties.properties")), "comment");
        properties.storeToXML(new FileOutputStream(new File("myProperties1.xml")), "comment");
    }
}
myProperties
#comment
#Fri Mar 09 10:11:42 CST 2018
w=zq
w1=zq1
w2=zq2

myProperties1



comment
zq
zq1
zq2

SortedMap接口和TreeMap实现类

TreeMap就是一个红黑树数据结构,每个key-value对即作为红黑树的一个节点。TreeMap存储key-value对时,需要根据key对节点进行排序。TreeMap可以保证所有的key-value对处于有序状态。TreeMap也可以自然排序和定制排序。

TreeMap中判断两个key相等的标准是:两个key通过compareTo方法返回0,TreeMap即认为这两个key是相等的。

如果使用自定义类作为TreeMap的key,且想让TreeMap良好的工作,则重写该类的equals方法和compareTo方法时保持一致的返回结果。

Set和Map的关系十分密切,java源代码就是先实现了HashMap,TreeMMap等集合,然后通过包装一个所有的value都为null的Map集合实现了Set集合类。

public class TreeMapTest {
    public static void main(String[] args) {
        TreeMap treeMap = new TreeMap<>();
        treeMap.put(12, 34);
        treeMap.put(-12, 34);
        treeMap.put(122, 34);
        treeMap.put(0, 34);
        //{-12=34, 0=34, 12=34, 122=34}
        System.out.println(treeMap);
    }
}
WeakHashMap实现类

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

public class WeakHashMapTets {
    public static void main(String[] args) {
        WeakHashMap weakHashMap = new WeakHashMap<>();
        weakHashMap.put(new String("1"), new String("1"));
        weakHashMap.put(new String("2"), new String("2"));
        weakHashMap.put(new String("3"), new String("3"));
        weakHashMap.put("4", new String("4"));
        //{4=4, 1=1, 2=2, 3=3}
        System.out.println(weakHashMap);
        System.gc();
        System.runFinalization();
        //{4=4}
        System.out.println(weakHashMap);
        //第四组key-value对的key是一个字符串直接量,系统会自动保留对该字符串对象的强引用,所以垃圾回收时不会回收他
    }
}    

如果需要使用WeakHashMap的key来保留对象的弱引用,则不要让该key所引用的对象具有任何强引用了否则将失去WeakHashMap的意义。

IdentityHashMap实现类

在IdentityHashMap中,当且仅当两个key严格相等key1 == key2时,IdentityHashMap才认为两个key相等,对于普通的HashMap而言,只要key1和key2通过equals方法比较返回true,且他们的hashCode值相等即可。

public class IdentityHashMapTest {
    public static void main(String[] args) {
        IdentityHashMap identityHashMap = new IdentityHashMap<>();
        identityHashMap.put(new String("r"),"r");
        identityHashMap.put(new String("r"),"r");
        identityHashMap.put("java", 0);
        identityHashMap.put("java", 1);
        //{java=1, r=r, r=r}
        System.out.println(identityHashMap);
        /**
         * 由于上面new String()的地址值不一样,IdentityHashCode返回值会不一样
         * 所以第一个String与第二个String并不一样
         * 但是java字符串直接量是一样的,所以第二次put就会覆盖原来的value
         */
    }
}
EnumMap实现类

EnumMap中的所有key都必须是单个枚举类的枚举值,创建EnumMap时必须显示或隐式指定它对应的枚举类。

EnumMap具有如下特征

EnumMap在内部以数组形式保存,所以这种实现形式非常紧凑,高效。

EnumMap根据key的自然顺序,即枚举值的定义顺序,来维护key-value对的顺序。

EnumMap不允许使用null作为key,但允许使用null作为value,如果试图使用null作为key时将抛出空指针异常。如果只是查询是否包含值为null的key,或只是删除值为null的key,都不会抛出异常。

创建EnumMap时必须指定一个枚举类,从而将该EnumMap和指定的枚举类关联起来。

public class EnumMapTest {
    public static void main(String[] args) {
        EnumMap enumMap = new EnumMap(EnumMaps.class);
        System.out.println(enumMap);//{}
        enumMap.put(WINTER, "4");
        enumMap.put(SPRING, "4");
        enumMap.put(SUMMER, "4");
        //{SPRING=4, SUMMER=4, WINTER=4}
        System.out.println(enumMap);
    }
}
各Map实现类的性能分析

HashMap和Hashtable的实现机制几乎一样,但由于Hashtable是一个古老的,线程安全的集合,因此HashMap通常比Hashtable要快。

TreeMap通常比HashMap,Hashtable要慢,尤其在插入删除key-value对时更慢,因为TreeMap底层采用红黑树来管理key-value对,红黑树的每个节点就是一个key-value对。

使用TreeMap有一个好处:TreeMap中的key-value对总是处于有序状态,无需专门进行排序操作。当TreeMap被填充之后,就可以调用keySet(),去的由key组成的Set,然后使用toArry()方法生成key的数组,接下来使用Arrays的binarySearch()方法在已排序的数组中快速地查询对象。

对于一般的应用场景,程序应该多考虑使用HashMap,因为HashMap正是为快速查询设计的:HashMap底层其实也是采用数组来存储key-value对。但如果程序需要一个总是排序好的Map时,则可以考虑使用TreeMap。

LinkedHashMap比HashMap慢一点,因为他需要维护链表来保持Map中key-value时的添加顺序。IdentityHashMap心梗没有特别出色之处,因为他采用与HashMap基本相似的实现,只是它使用==而不是equals方法来判断元素相等。EnumMap的性能最好,但它只能使用同一个枚举类的枚举值作为key。

HashSet和HashMap的性能选项

对于HashSet及其子类而言,它们采用hash算法来决定集合中元素的存储位置,并通过hash算法来控制集合的代销;对于HashMap,Hashtable及其子类而言,他们采用hash算法来决定Map中key的存储,并通过hash算法

设置不可变集合
public class NoModifyCollections {
    //不可变对象都不可以增加和删除操作
    public static void main(String[] args) {
        //创建一个空的不可变的List对象
        List emptyList = Collections.emptyList();
        //创建一个还有一个元素,且不可改变的Set对象
        Set singleton = Collections.singleton("java");
        HashMap map = new HashMap<>();
        map.put("", "");
        //返回普通Map对象对应的不可变版本
        Map map2 = Collections.unmodifiableMap(map);
    }
}

Java9 增加的不可变集合
public class NoModifyCollections9 {
    public static void main(String[] args) {
        List of = List.of("");
        Set of2 = Set.of("");
        Map of3 = Map.of("1","2","3","4");
        System.out.println(of3);//{3=4, 1=2}
        Map ofEntries = Map.ofEntries(Map.entry("", ""));
        System.out.println(ofEntries);//{=}
    }
}
繁琐的接口Enumeration

繁琐的接口Enumeration:只能遍历Vector和Hashtable这种老java类

public class EnumerationTeST {
    public static void main(String[] args) {
        Vector vector = new Vector();
        vector.add("3");
        vector.add("2");
        vector.add("1");
        Enumeration elements = vector.elements();
        while (elements.hasMoreElements()) {
            System.out.println(elements.nextElement()); // 3 2 1 
        }
    }
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/69317.html

相关文章

  • Java编程基础19——Map集合&斗地主案例

    摘要:使用默认随机源对指定列表进行置换。将集合排序使用二分搜索法搜索指定列表,以获得指定对象根据元素的自然顺序,返回给定的最大元素。 1_Map集合概述和特点 A:Map接口概述 查看API可以知道: 将键映射到值的对象 一个映射不能包含重复的键 每个键最多只能映射到一个值 B:Map接口和Collection接口的不同 Map是双列的,Collection是单列的 Map...

    ygyooo 评论0 收藏0
  • Java集合框架——Map接口

    摘要:第三阶段常见对象的学习集合框架集合在实际需求中,我们常常会遇到这样的问题,在诸多的数据中,通过其编号来寻找某一些信息,从而进行查看或者修改,例如通过学号查询学生信息。面试题和的区别是单列集合的顶层接口,有子接口和。 第三阶段 JAVA常见对象的学习 集合框架——Map集合 showImg(https://segmentfault.com/img/remote/1460000019683...

    princekin 评论0 收藏0
  • Map学习二之LinkedHash,HashTable,计算一个给定字符串的每个字符出现的次数

    package com.itheima.demo03.Map; import java.util.HashMap;import java.util.LinkedHashMap; /* java.util.LinkedHashMap entends HashMap Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。 底层原理: 哈希表+链表(记录元素的顺序) */public cla...

    Rocture 评论0 收藏0
  • 1、Map接口 2、模拟斗地主洗牌发牌

    摘要:中的集合称为单列集合,中的集合称为双列集合。洗牌通过数字完成洗牌发牌发牌将每个人以及底牌设计为将最后张牌直接存放于底牌,剩余牌通过对取模依次发牌。存放的过程中要求数字大小与斗地主规则的大小对应。 01Map集合概述 A:Map集合概述: 我们通过查看Map接口描述,发现Map接口下的集合与Collection接口下的集合,它们存储数据的形式不同  a:Collection中的集...

    付伦 评论0 收藏0
  • JAVA-集合类概述(笔记)

    摘要:通常使用矢代器来实现遍历集合。将集合中指定索引位置的对象改成指定的对象。三元运算如果否则集合没有继承接口,它是提供到的映射,来决定遍历对象的顺序。 showImg(/img/bVldsA); 1.Collection接口是层次结构中的跟接口。Collection接口通常不能直接使用,但该接口提供了添加元素,删除元素,管理数据的方法。 Collection通常使用矢代器(Iterato...

    wangbinke 评论0 收藏0
  • Map学习一之基本操作方法put,get,remove,containsKey

    摘要:返回值存在返回对应的值不存在返回创建集合对象赵丽颖杨颖林志玲杨颖迪丽热巴把指定的键所对应的键值对元素在集合中删除,返回被删除元素的值。 package com.itheima.demo01.Map; import java.util.HashMap;import java.util.Map; /* java.util.Map集合 Map集合的特点: 1.Map集合是一个双列集合...

    TwIStOy 评论0 收藏0

发表评论

0条评论

Little_XM

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<