Java集合(下)

本文深入探讨Java中的映射概念,包括HashMap和TreeMap的特点及应用,如何高效更新映射项,以及WeakHashMap、LinkedHashSet和LinkedHashMap的特殊用途。同时介绍了EnumSet和EnumMap在枚举类型中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

映射

映射用来存放键值对。

基本映射操作

Java中有HashMapTreeMap两个类实现了Map接口,前者基于散列表,对建进行散列,后者用键的整体顺序对元素进行排序,并将其组织成搜索树,效率前者快一点。
使用Map接口的get方法时,如果key不存在,则会返回null,如果希望有一个更好的默认值,可以使用getOrDefault(Object,Object),当没有结果是,使用第二个参数。

更新映射项

以自增1为例,有以下几种方法

  • counts.put(word,counts.get(word) + 1);
    这种写法容易造成空指针异常,在word第一次出现的时候

  • counts.put(word,counts.getOrDefault(word,0) + 1);

  • counts.putIfAbsent(word,0);
    counts.put(word,counts.get(word) + 1); //保证非空
    所以以后不应该再用:

    1
    2
    3
    
    if(counts.get(word) == null){
        counts.put(word,0);
    }
    
  • counts.merge(word, 1, Integer::sum);
    这个写法比较高级,书上没写太清楚,我看了下源码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    default V merge(K var1, V var2, BiFunction<? super V, ? super V, ? extends V> var3) {
        Objects.requireNonNull(var3);
        Objects.requireNonNull(var2);
        Object var4 = this.get(var1);
        Object var5 = var4 == null ? var2 : var3.apply(var4, var2);
        if (var5 == null) {
            this.remove(var1);
        } else {
            this.put(var1, var5);
        }
    
        return var5;
    }
    

先取key为word的值,如果为空,直接将第二个参数作为word的值put到map中,如果不为空,根据给出的函数式接口(这里我们使用Integer::sum方法)计算原值和给出的第二个参数的和并返回。

映射视图

Set<K> keySet() 键集
Collection<V> values() 值集
Set<Map.Entry<K,V>> entrySet() 键值对集

在键集视图上调用迭代器的remove方法,实际上会从映射中删除这个键和与它关联的值,并且不能向键集中增加元素。
如果试图调用add方法,则会报UnsupportedOperationException异常。

弱散列映射WeakHashMap

JVM的垃圾回收机制中,如果一个对象没有任何引用了的话,就会在下一次GC中被回收,如果在映射中,一个键对应的值如果没有任何引用,是不会被回收的,因为垃圾回收针对的是对象,这个值依附Map存在,只要Map还存在引用没有被回收,这个值也不会被回收。
WeakHashMapHashMap的区别是,WeakHashMap使用弱引用(Weak Reference)保存键。Weak Reference对象将引用保存到另外一个对象中这里就是散列键。
当垃圾收集器发现某对象只被一个弱引用引用时,会将这个对象回收,并且会将这个散列键放入一个队列中,WeakHashMap周期性的检查这个队列,如果发现有弱引用进入了队列,就删除对应的条目。

链接散列集LinkedHashSet与链接映射 LinkedHashMap

上篇说到的基于散列表Set和Map中的元素都是无序的,但是LinkedHashSet LinkedHashMap中的元素是有序的,因为这两个类型在实现散列表的基础上加了一个双向链表。
当条目插入到表中时,就会并入双向链表中
链接散列映射将用访问顺序而不是插入顺序对映射条目进行迭代。也就是说,每当读写链接散列映射中的元素时,受到影响的条目会从当前位置删除,并放到双向链表的尾部,由于双向链表和散列表独立,所以并不会影响原来的存储结构。

枚举集与映射EnumSet EnumMap

枚举集EnumSet用来存储枚举类型,使用静态工厂方法构造枚举集
枚举映射EnumMap表示键为枚举类型的映射
EnumMap<WeekDay,Integer> weekDays = new EnumMap<>(WeekDay.class);

EnumSet类源码中,对这个类的定义有如下:

1
public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E> implements Cloneable, Serializable {

其中<E extends Enum<E>>表示类型参数扩展了Enum<E>,意思就是,E是枚举类型。所有的枚举类型都是扩展了泛型Enum类。

视图与包装器

除了之前介绍的一些对Collection和Map的实现类,Java类库中还有很多能够直接获得的集合对象,比如,给定一个Map,Map中有一个方法,可以返回所有键的集合,通过这个集合可以对原映射操作。
这种集合就叫做视图。

轻量级集合包装器

Arrays.asList

子范围

subList subSet

不可修改的视图

当得到一个unmodifiableCollection时,它的equals方法不调用底层集合的equals方法,直接进行对象判等。

同步视图

Map<String, Integer> map = Collections.synchronizedMap(new HashMap<String,Integer>());
这个方法实际上接收一个Map,返回一个SynchronizedMap类型的对象。在使用get,put方法时,下一个操作必须等到上一个操作结束之后才能进行。

受查视图

Collections.checkedList(strings,String.class);

视图的Add方法检查插入的对象是否属于给定的类,如果不属于,直接抛出ClassCastException异常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值