Guava-HashMultiset

前言

在了解了非修改性集合后,接着了解Guava中提供的一种基于Multiset接口的新的集合类型,基于该接口,可以新增、删除元素、对集合中的元素进行计数,使用非常方便。

HashMultiset

HashMultiset实现了AbstractMapBasedMultiset抽象类,其是实现Multiset的子类,类继承图结构如下。

AbstractMapBasedMultiset作为HashMultiset的父类,实现了具体的添加、删除、计数的逻辑。

示例


package com.hust.grid.leesf.guavalearning;

import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;

public class HashMultisetTest {
    public static void main(String[] args) {
        HashMultiset<String> multiset = HashMultiset.create();
        multiset.add("leesf");
        multiset.add("leesf");
        traverse(multiset);
        System.out.println("count =" + multiset.count("leesf"));
        multiset.remove("leesf");
        System.out.println("count = " + multiset.count("leesf"));
        traverse(multiset);
        multiset.remove("leesf");
        traverse(multiset);
        multiset.remove("dyd");
    }

    public static void traverse(HashMultiset<String> multiset) {
        for (Multiset.Entry entry : multiset.entrySet()) {
            System.out.println(entry.getElement() + " : " + entry.getCount());
        }
    }
}



运行结果如下:

leesf : 2
count =2
count = 1
leesf : 1

可以看到其可以多次添加同一个Key,同一个Key只会被存储一次(Hash属性),但是其值(Count)会自动添加,当进行移除时,也可以看到若元素的Count大于1,则只会减少Count,当Count减少到为0,就会移除该元素。

add方法

首先会调用AbstractMultiset类的add(@Nullable E element)方法,然后该方法会调用AbstractMapBasedMultisetadd(@Nullable E element, int occurrences)方法,其源码如下。


  @Override public int add(@Nullable E element, int occurrences) {
    if (occurrences == 0) { // 次数为0,直接返回现有集合中该element出现的次数
      return count(element);
    }
	// 保证出现次数不能为负数
    checkArgument(
        occurrences > 0, "occurrences cannot be negative: %s", occurrences);
	// 获取该元素对应的Count对象
    Count frequency = backingMap.get(element);
    int oldCount;
    if (frequency == null) { // 不存在,表示第一次添加该元素
	  // 之前出现次数为0
      oldCount = 0;
	  // 添加至backingMap中,该集合存放元素和出现次数(Count对象)
      backingMap.put(element, new Count(occurrences));
    } else { // 集合中存在该元素
	  // 获取之前出现的次数
      oldCount = frequency.get();
	  // 计算新的次数
      long newCount = (long) oldCount + (long) occurrences;
	  // 保证不超过最大阈值
      checkArgument(newCount <= Integer.MAX_VALUE,
          "too many occurrences: %s", newCount);
	  // 获取然后增加次数
      frequency.getAndAdd(occurrences);
    }
	// 改变集合大小
    size += occurrences;
	// 返回集合中元素之前的出现次数
    return oldCount;
  }

可以看到其主要是通过backingMap提供支持,其为HashMap<String, Count>类型,Count对象中封装了出现的次数和对次数进行获取和改变的操作。

remove方法

首先会调用AbstractMultiset类的remove(@Nullable E element)方法,然后该方法会调用AbstractMapBasedMultisetremove(@Nullable E element, int occurrences)方法,其源码如下。


    @Override public int remove(@Nullable Object element, int occurrences) {
    if (occurrences == 0) { //次数为0,直接返回现有集合中该element出现的次数
      return count(element);
    }
	// 保证出现次数不能为负数
    checkArgument(
        occurrences > 0, "occurrences cannot be negative: %s", occurrences);
	// 获取该元素对应的Count对象	
    Count frequency = backingMap.get(element);
    if (frequency == null) { // 不存在,表示第一次添加该元素
      return 0; // 直接返回0
    }
	// 获取之前出现次数
    int oldCount = frequency.get();
	// 移除的次数
    int numberRemoved;
    if (oldCount > occurrences) { // 集合之前出现的次数大于要删除的次数
	  // 移除的次数为删除的次数
      numberRemoved = occurrences;
    } else { // 集合之前出现的次数小于等于要删除的次数
	  // 移除的次数为之前出现的次数
      numberRemoved = oldCount;
	  // 移除该元素
      backingMap.remove(element);
    }
	// 更新存在的次数
    frequency.addAndGet(-numberRemoved);
	// 改变集合大小
    size -= numberRemoved;
	// 返回集合中元素之前的出现次数
    return oldCount;
  }

可以看到当需要删除的次数大于等于集合中已存在的次数时,会直接删除元素,否则,只会减少该元素的次数;当删除不存在的元素时,会直接返回0

总结

本篇博文讲解了HashMultiset的使用,通过该类可轻松实现对集合中某元素进行技术的功能,其源码也相对简单。