博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java集合-Map
阅读量:6324 次
发布时间:2019-06-22

本文共 13538 字,大约阅读时间需要 45 分钟。



Java8增强的Map集合

  1. Key-value都可以是任何引用类型的数据,Map的Key不允许重复即同一个Map对象的任何两个key通过equals方法比较总是返回false。
  2. 如果把Map里的所有key放在一起来看,他们就组成了一个Set集合:所有key没有顺序,key与key之间不能重复,实际上Map确实包含了一个keySet方法,用来返回Map里所有key组成的Set集合。
  3. Map的这些实现类和子接口中key集的存储形式和对应Set集合中元素的存储形式完全相同。
  4. 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集合。
  5. 如果把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实现类

  1. Hashtable和HashMap区别

    • Hashtable是一个线程安全的Map实现,但HashMap是线程不安全的实现,所以HashMap比Hashtable的性能高一点,但如果有多个线程访问同一个Map对象时,使用Hashtable实现类会更好、
    • Hashtable不允许使用null作为key和value,如果试图把null值放入Hashtable里,将引发空指针异常,但HashMap可是使用null作为key和value、
  2. 为了成功的在HashMap,Hashtable中存储,获取对象,用作key的对象必须实现hashCode方法和equals方法。
  3. 类似于HashSet,HashMap,Hashtable判断两个key相等的标准也是:两个key通过equals方法返回true,两个key的hashCode值也相等。
  4. 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。
  1. 与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实现类

  1. LinkedHashMap也使用双向链表来维护key-value对的次序,其实只需要考虑key的次序,该链表负责维护Map的迭代顺序,迭代顺序与key-value对的插入顺序保持一致。
  2. 因为他使用链表来维护内部顺序,所以在迭代访问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读写属性文件

  1. Properties是Hashtable类的子类
  2. 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 2018w=zqw1=zq1w2=zq2myProperties1
comment
zq
zq1
zq2

SortedMap接口和TreeMap实现类

  1. TreeMap就是一个红黑树数据结构,每个key-value对即作为红黑树的一个节点。TreeMap存储key-value对时,需要根据key对节点进行排序。TreeMap可以保证所有的key-value对处于有序状态。TreeMap也可以自然排序和定制排序。
  2. TreeMap中判断两个key相等的标准是:两个key通过compareTo方法返回0,TreeMap即认为这两个key是相等的。
  3. 如果使用自定义类作为TreeMap的key,且想让TreeMap良好的工作,则重写该类的equals方法和compareTo方法时保持一致的返回结果。
  4. 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实现类

  1. 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实现类

  1. 在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实现类的性能分析

  1. HashMap和Hashtable的实现机制几乎一样,但由于Hashtable是一个古老的,线程安全的集合,因此HashMap通常比Hashtable要快。
  2. TreeMap通常比HashMap,Hashtable要慢,尤其在插入删除key-value对时更慢,因为TreeMap底层采用红黑树来管理key-value对,红黑树的每个节点就是一个key-value对。
  3. 使用TreeMap有一个好处:TreeMap中的key-value对总是处于有序状态,无需专门进行排序操作。当TreeMap被填充之后,就可以调用keySet(),去的由key组成的Set,然后使用toArry()方法生成key的数组,接下来使用Arrays的binarySearch()方法在已排序的数组中快速地查询对象。
  4. 对于一般的应用场景,程序应该多考虑使用HashMap,因为HashMap正是为快速查询设计的:HashMap底层其实也是采用数组来存储key-value对。但如果程序需要一个总是排序好的Map时,则可以考虑使用TreeMap。
  5. LinkedHashMap比HashMap慢一点,因为他需要维护链表来保持Map中key-value时的添加顺序。IdentityHashMap心梗没有特别出色之处,因为他采用与HashMap基本相似的实现,只是它使用==而不是equals方法来判断元素相等。EnumMap的性能最好,但它只能使用同一个枚举类的枚举值作为key。

HashSet和HashMap的性能选项

  1. 对于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

  1. 繁琐的接口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 } }}

转载地址:http://vtmaa.baihongyu.com/

你可能感兴趣的文章
css-从笔试题中看知识
查看>>
更好用的集群限流功能,Sentinel 发布 v1.4.2
查看>>
HTTP 简介
查看>>
Flutter 1.2 发布,添加应用内支付和 App Bundles
查看>>
春招必看一位老学长的真实互联网校招求职心路历程~
查看>>
稳定与非稳定版本软件的Docker Image构建策略
查看>>
vue-cli3+typescript初体验
查看>>
NATS--NATS Streaming持久化
查看>>
react下实现一个PDF展示组件
查看>>
实际工作中用不上数据结构和算法吗?
查看>>
分布式理论之BASE理论
查看>>
React Fiber源码分析 第三篇(异步状态)
查看>>
gRPC遇见.NET SDK和Visual Studio:构建时自动生成编码
查看>>
Android 应用防止被二次打包指南
查看>>
ES6的Symbol竟然那么强大,面试中的加分点啊
查看>>
什么是敏捷软件开发?
查看>>
【Redis源码分析】Redis中的LRU算法实现
查看>>
从头开始学习Vuex
查看>>
阿里云文件存储的高性能架构演进之路
查看>>
js正则表达式学习笔记
查看>>