Guava的基础功能与集合

拥抱Guava之集合操作 深入了解Google Guava库的集合操作功能,本文介绍了其强大的特性,包括不可变集合和高效工具类。通过实例演示,为Java开发者提供全面的集合操作解决方案 阅读详情
AI摘要

Guava是Google的Java核心类库,提供了丰富的集合类型、不可变集合、排序工具、对象方法以及异常处理。它简化了Java编程,如避免null、预条件检查、排序操作、通用对象方法等。Guava还引入了Multiset、Multimap、Bimap等新集合类型,增强了异常处理的便捷性,并提供了如Throwables和Objects的实用工具类。

Guava

文章目录

一、 guava 是什么

guava是来自Google的Java核心类库。包含了新的集合类型(例如:复合map、复合set)、不可变集合,以及一些对于并发、I/O、hashing、缓存、原型、字符串等的通用功能。guava被广泛使用在Google的项目中,也被广泛的使用在其他公司里。

二、 基础功能:更开心的使用Java语言

2.1 使用和避免null

null 是一个模棱两可的概念,能引起让人困惑的错误。很多Guava的通用功能在遇到null的时候会拒绝并快速失败。而不是盲目的接受它们。

####(1)使用和避免null:Optional

粗心使用null可能造成令人震惊的各种错误。

另外,null是一个令人不愉快的模糊概念,很少能从返回的null值明确的推测出其代表的含义。例如Map.get(key),返回null,既可以代表在map中的值是null,也可以代表value不再map中。null即可以代表失败,也是代表成功,它能代表几乎任何东西。使用null以外的东西,可以让意思更加明确。

Guava提供了很多特征,不仅帮助你让在需要使用null的时候轻松使用null,也让你避免使用null。

Optional:

  • 创建Optionnal,静态方法

    • Optional.of(T t) 当遇到null值当时候,就会快速失败;
    • Optional.obsent() 创建某一个缺少某个值当类型当对象;
    • Optional.fromNullable(T t) 将一个可能为非空的引用转换为Optional;
  • 查询方法,非静态方法

    • optional.isPresent() 判断是否存在
    • optional.get() 如果不存在将抛出异常
    • optional.or(T t) 如果不存在返回定义的默认值
    • optional.orNull() 如果不存在返回null
    • optional.asSet() 返回一个不可变的集合,有一值,或者一个空集合
  • 重点是什么?

    除了通过null值一个名称增加了可读性,最大的优势的它的操作是白痴操作。如果你想让你的程序在任何时候都能编译通过,它强迫你去考虑值不存在的情况,因为你不得不积极的展开Optional并去处理这种情况。

    它是极其相关的,当返回的值既可能是存在也可能不存在的时候。比起你要实现other.method方法的时候忘记a可能为null值,你更容器忘了other.method(a, b)的返回值是null。让返回值是Optional类型,就会避免这种情况的发生。

  • 便利的方法

    • MoreObjects.firstNonNull(T t, T t2) 无论什么时候你想让一个null值被一个默认值代替,使用MoreObjects.firstNonNull。如果两个值都为空,将报空指针异常。

      如果使用Optional,则有一个更好的代替方案,or(T t)

    一些用于处理String类型null的方法,是在Strings中提供,具体来说,我们提供了如下的名称:

    • Strings.nullToEmpty(String)
    • Strings.isNullOrEmpty(String)
    • Strings.emptyToNull(String)

    我们想要强调这些方法主要是为了面对令人不愉快的APIs交互,它们等同于null 字符串或空字符串。每次你要写代码合并null 字符串和空字符串的时候,Guava团队都在哭泣。(如果将null字符串和空字符串作为不同的东西对待,那是好多,如果将它们作为相同的东西对待就是会有令人不安的代码味道。)

2.2 先决条件:Preconditions

让方法更容易测试先决条件。

(1)Preconditions 类

Guava提供了很多先决条件的检查工具在Preconditions类中。强烈推荐通过静态导入的方式使用它们。

每一个方法都有三个变种:

  • 没有额外的参数,任何异常将会抛出,不带任何错误信息;

  • 带有一个额外的Object参数,任何异常将会抛出,带有一个object.toString的错误消息;

  • 带有一个额外的String类型的参数,并带有任意数量的额外的Object参数。这种行为有时候更像printf,但是它为了GWT的兼容性和高效性,仅仅允许%s标识符。

    注意:checkNotNull、checkArgument、checkState又很多的重载方法,获取原始类型和对象参数,而不是一个可变数组。在绝大多数情况下,这种调用可以避免上述调用时进行原始类型的装箱和可变数组的分配。

第三种变体的示例代码:

import static com.google.common.base.Preconditions.*;
public class UsingAndAvoidNull {
    public void preconditionTest(int i, int j) {
        checkArgument(i>0, "Argument was %s but expected nonnegative", i);
        checkArgument(i>j, "Expected i > j, but %s >= %s", i, j);
    }
}
方法签名(不包含额外的参数)描述在失败的时候抛出异常
checkArgument(boolean)检查boolean是true,用于验证方法的参数。IllegalArgumentException
checkNotNull(T)检查值不为null,如果在行内使用checkNotNull(value),将直接返回value值。NullPointerException
checkState(boolean)检查对象的状态,不依赖方法的参数。例如,一个Iterator可以使用这个方法检查next有值能够调用,在调用remove之前IllegalStateException
checkElementIndex(int index, int size)检查索引是有效的,对于list、String、或者指定大小的数组。元素的索引从0(包含)到size(不包含)。不能直接传递list、string、或数组。仅传递它的大小,返回索引值。IndexOutOfBoundsException
checkPositionIndex(int index, int size)检查是有一个位置,对于list、string、或指定大小的数组。位置的值从0(包含)到size(不包含)。不能直接传递list、string、数组,只需要传递它们的大小。最后返回index。IndexOutOfBoundsException
checkPositionIndexes(int start,int end, int index)检查index是否在[start, end]范围内,其中end值要大于等于start。IndexOutOfBoundsException

可变参数“printf-style”风格的异常消息。这个优势是我们推荐使用checkNotNull,而不是Objects.requireNotNull的原因。

建议将先决条件分成不同的行,它能帮助你在调试期间定位出哪个先决条件失败了。此外,你应该提供有用的错误信息。

(2)有条件的失败

一个有条件的失败或者运行时检查,是任何一段代码当且仅当一个boolean条件成立的时候抛出异常。在好的软件设计中这种代码很常见。

一个非常容易的处理,可以用相同的方法:if(!condition) {throw new RunTimeException();} 。但是如果你花一点时间去考虑你正在执行的检查性质,然后用更合适的方式处理它,就能让你的代码更容易理解,错误也更容易诊断。

这里有一些重要的运行时检查:

  • 先觉条件的检查。

    确认调用的公共方法是服从方法规范要求的。例如,一个sqrt函数,仅接受非负参数。

    IllegalArgumentException、IllegalStateException

  • 传统的断言检查。

    仅应该在以某种方式类本身被破坏的时候失败。

    assert、AssertionError

  • 确认检查。

    当你对一个要使用的API严重缺乏信心的时候,要对它进行检查。

    VerifyException

  • 测试断言。仅仅在测试类中被发现,确保测试代码是符合规范要求的。

    注意,这类断言的代码和生产代码的断言几乎没有共同之处。

    assertThat、assertEquals、AssertionError

  • 不可能条件检查。它是不可能失败,除非我们代码被修改了,或者严重违反了底层平台的行为。

    AssertionError

  • 一个异常的结果。方法没有提供预期的结果

2.3 排序:Ordering

Guava有强大的Comparator类。

从本质上讲,Ordering 无非是一个Comparator实现。Ordering 依赖于Collector的方法(例如,Collections.max)。此外Ordering通过链式方法来增强和调整现有的比较器。

(1)常见的排序通过静态方法提供
  • Ordering.natural() 对可比较类型进行自然排序
  • Ordering.usingToString() 根据toString()返回的字符串表示形式的字典顺序比较对象。
(2)将一个预先存在的Comparator变成Ordering
  • Ordering.from(Comparator)
(3)创建一个自定义的Ordering:直接扩展Ordering
        Ordering byLenOrdering = new Ordering<String>() {
            @Override
            public int compare(String s1, String s2) {
                return Integer.compare(s1.length(), s2.length());
            }
        };
(4)比较器链

通过包装Ordering获取派生的顺序。一些常用的变体包含如下:

  • reverse() 逆序

  • nullsFirst() null值放到最前面,非空值按照Ordering定义的排序进行排序;

  • nullsLast() null值放在最后,非空值按照Ordering定义的排序进行排序;

  • compound() 组合两个比较器,当第一个比较器无法比较大小时,采用第二个比较器进行比较;

    // 按照字符串的长度进行比较,如果长度相同按照字母表顺序进行比较
    Collections.sort(names, byLenOrdering.compound(Ordering.usingToString()));
    
  • lexicographical() 对Iterator列表进行排序,类比Ordering.usingToString() ,这里每个可对比的单元是Iterator中的一个元素。

            List<List<String>> groups = new ArrayList<>();
            groups.add(new ArrayList<>(Arrays.asList("aa", "ff")));
            groups.add(new ArrayList<>(Arrays.asList("aa", "bb", "cc")));
            System.out.println(groups);  // [[aa, ff], [aa, bb, cc]]
            Collections.sort(groups, Ordering.usingToString().lexicographical());
            System.out.println(groups);  // [[aa, bb, cc], [aa, ff]]
    
  • onResultOf(Function) 把比较器的元素使用Function函数转化成一个值result,再对这个值应用Ordering的比较方法。result的排序顺序就是最后的排序顺序。

例如,你要对Foo列表进行排序:

class Foo {
  String sortedBy;
  int notSortedBy;
}

Ordering<Foo> ordering = Ordering.natural().nullsFirst().onResultOf((Foo foo)->foo.sortedBy);
(5)比较器链的调用:从右往左的调用
Ordering<Foo> ordering = Ordering.natural().nullsFirst().onResultOf((Foo foo)->foo.sortedBy);

比如上面的调用器链,先查找Foo的sortedBy字段,然后将所有null元素移动到最前面,再对剩下的顺序进行自然排序。

之所以,出现这个从右往左的调用方式,是因为每个调用链都是将前面一个Ordering包装成一个新的Ordering。

注意:compound() 是调用链里的例外,它是从左往右调用。

调用链太长,不容易理解。推荐把调用链控制在三个以内。

####(6)应用

  • greatestOf(Iterable iterable, int k) 对元素按照从大到小排序,并返回前k个元素

    List<Integer> result = Ordering.natural().greatestOf(new ArrayList<>(Arrays.asList(3, 5, 1)), 2);
    
  • leastOf(Iterable iterable, int k) 对元素按照从小到大排序,并返回前k个元素

  • isOrdered(Iterable iterable) 测试是否为按照递增的顺序排序

    boolean ordered = Ordering.natural().isOrdered(result);
    
  • isStrictlyOrdered(Iterable iterable) 测试是否按照递减的顺序排序

  • sortedCopy(Iterable iterable) 返回一个新的已经排序的列表,原来的列表顺序不会变

            List<Integer> nums = new ArrayList<>(Arrays.asList(4, 1, 3));
            List<Integer> resultCopy = Ordering.natural().sortedCopy(nums);
            System.out.println(resultCopy); // [1, 3, 4]
            System.out.println(nums); // [4, 1, 3]
    
  • immutableSortedCopy(Iterable iterable) 类似sortedCopy,只不过返回一个不可变的副本

  • min(E e1, E e2) 返回最小的值,如果两个值相等,返回第一个元素。

    Integer min = Ordering.natural().min(5, 1);
    
  • max(E e1, E e2) 返回最大值,如果两个值相等,返回第一个元素。

  • min(E, E, E...) 对比多个值,返回最小值,如果有多个最小值,返回第一个

  • max(E, E, E...) 对比多个值,返回最大值,如果有多个最大值,返回第一个

  • min(Iterable) 、max(Iterable) 返回列表中的最小值或最大值,如果列表为空,抛出异常。

2.4 Objects中通用的对象方法:equals、hashCode、toString

简化了实现对象的方法,例如hashcode()、toString()。

(1)Objects.equal()

可以避免在比较时由于null值带来麻烦。在JDK7中也引入的Objects,里面的equals() 方法是等价的。

Objects.equal("a", "a"); // returns true
Objects.equal(null, "a"); // returns false
Objects.equal("a", null); // returns false
Objects.equal(null, null); // returns true
(2)Objects.hashCode(filed1, filed2,...,filedn)

对一系列顺序的字段进行hash。

在JDK7中引入的Objects中,里面的hash() 方法和上面是等价的。

(3)toString() :MoreObjects.toStringHelper()

toString() 方法对于调试代码没有用,但很难写,可以使用MoreObjects.toStringHelper()方法来创建一个有用的toString方法。

// MyObject{x=1, y=cc}
MoreObjects.toStringHelper("MyObject")
                .add("x", 1)
                .add("y", "cc")
                .toString();
(4)compare/compareTo:ComparisonChain

在实现Comparator 或 Comparable的时候非常痛苦,可以使用ComparisonChain来进化工作:

    @Override
    public int compareTo(Product o) {
        return ComparisonChain.start()
                .compare(this.name, o.name)
                .compare(this.num, o.name)
                .compare(this.typeEnum, o.typeEnum, Ordering.natural())
                .result();
    }

ComparisonChain 采用懒工作模式:它执行比较,直到找到一个非零的结果,然后忽略进一步的输入。

2.5 异常:Throwables

Guava中的Throwables能够简化异常的处理。

(1)传播异常
  • Throwables.propagateIfPossible(Throwable, Class<X extends Throwable>) throws X; 只有当它是RuntimeException、Error、或X的时候,它才会按照原样抛出异常
  • Throwables.throwIfInstanceOf(Throwable, Class<x extends Exeption>) throws X; 当且仅当它是X类型的异常时,按照原样传播可抛出异常
  • Throwables.throwIfUnchecked(Throwable) 仅当它是RuntimeExeption或Error的时候,才按照原样抛出异常
(2)异常的因果关系
  • Throwables.getRootCause(Throwable) 获取根部异常
  • Throwables.getCausalChain(Throwable) 获取异常链
  • Throwables.getStackTraceAsString(Throwable) 将异常转化成字符串
            Throwable throwable = new IllegalArgumentException(new ArrayStoreException());
            Throwable rootCause = Throwables.getRootCause(throwable); // java.lang.ArrayStoreException
            System.out.println(rootCause);
						// [java.lang.IllegalArgumentException: java.lang.ArrayStoreException, java.lang.ArrayStoreException]
            System.out.println(Throwables.getCausalChain(throwable));
            /*
            java.lang.IllegalArgumentException: java.lang.ArrayStoreException
	at com.hef.guava.baseutilities.ThrowDemo.main(ThrowDemo.java:16)
Caused by: java.lang.ArrayStoreException
	... 1 more
            */
            System.out.println(Throwables.getStackTraceAsString(throwable));

三、集合:Collections

Guava扩展了Java的集合生态系统,这是Guava中非常成熟和流程的一部分。

3.1 不可变集合:Immutable

(1)不可变集合的好处
  • 被不信任的类库使用是安全的
  • 线程安全:能够被多个线程实现,而没有共享问题;
  • 节省内存和时间
  • 可以作为常量使用

为了集合对象创建不可变的副本是一个好的防御性的编程策略。Guava为每一个Collection类型(包括Guava自己的Collection类)都提供了简单易用的不可变集合版本。

(2)JDK的Collections.unmodifiableXXX 产生不可变集合存在的问题
  • 笨重、冗长
  • 不安全:只有当没有人持有原始集合引用的时候,它返回的结合才真正是不可变的;
  • 低效:数据结构仍然保留着可变集合的开销,包括并发修改检查、在hash表中额外的空间等。
(3)不可变集合的使用场景(Guava的不可变集合中不允许null值)

当你不期望集合被修改,或者期望集合一致不变,一个好的策略是采用防御性拷贝,让其变成不可变集合。

重要:在Guava的不可变集合中,不允许有null值。如果你想使用带有null的不可变集合,可以使用Collections.unmodifiableXXX。

(4)创建不可变集合的几个方法
  • 通过copyOf方法,例如:ImmutableSet.copyOf(set)

  • 使用of 方法,例如:ImmutableSet.of

    ImmutableMap.of("name", "world", "where", "beijing");
    ImmutableSet.of("aa", "bb")
    
  • 使用builder方法:

    ImmutableSet<String> result = ImmutableSet.<String>builder().add("aaa").add("ccc").build();
    

除了有顺序的集合,顺序是根据存入数据结构的先后顺序。

(5)copyOf 非常的聪明:能在安全的情况下,避免复制数据结构。

ImmutableXXX.copyOf 避免线性时间的复制:

  • 在恒定的时间,使用底层的数据结构。例如,ImmutableSet.copyOf(ImutableList), 不能再固定时间完成。
  • 它不会导致内存泄露。例如,如果有一个ImmutableList<String> hugeList,使用ImmutableList.copyOf(hugeList.subList(0, 10)),将会执行一个明确的拷贝。从而避免持有对hugeList不必要的引用;
  • 不会改变语义。例如,ImmutableSet.copyOf(myImmutableSortedSet)将执行明确的拷贝,因为hashCode和equals在ImmutableSet和ImmutableSorted中的语义是不一样的。

这将能为防御性编码风格带来最小的性能负担。

(6)asList

便可变集合通过asList方法就能得到一个ImmutableList 。

(7)J可变集合接口与Guava中的不可变集合对应
JDK中的接口JDK 还是 GuavaGuava中的不可变集合版本
CollectionJDKImmutableCollection
ListJDKImmutableList
SetJDKImmutableSet
SortedSet/NavigableSetJDKImmutableSortedSet
MapJDKImmutableMap
SortedMapJDKImmutable
MultiSetGuavaImmutableMultiSet
SortedMultiSetGuavaImmutableSortedMultiSet
MultimapGuavaImmutableMultimap
ListMultimapGuavaImmutableListMultimap
SetMultimapGuavaImmutableSetMultimap
BiMapGuavaImmutableBiMap
ClassToInstanceMapGuavaImmutableClassToInstanceMap
TableGuavaImmutableTable

3.2 新的集合类型

Guava中定义的很多新的集合,它们被广泛的使用。这些新的集合遵循JDK的集合接口约定。

(1)Multiset (接口)
Multiset 的使用

一个场景:统计单词在文章中出现的次数。传统的做法如下:

        Map<String, Integer> result = new HashMap<>();
        for (String word : words) {
            if (result.containsKey(word)) {
                result.put(word,result.get(word)+1);
            }else {
                result.put(word, 1);
            }
        }

这样做容易出错,还不利于统计出一些有用的信息,例如,单词的总数。

使用Multiset就可以很简单的这样写:

        Multiset<String> set = HashMultiset.create();
        set.addAll(words);
  • 当被当作了一个普通的Collection的时候,Multiset的行为类似一个无序的ArrayList;
    • 调用add(E)方法,添加一个指定的元素;
    • iterator() 方法,遍历每个元素每次出现(元素每出现一次就会被遍历一次);
    • size() 方法,获取所有元素出现的总次数;
    • addAll(Collection) 添加多个元素添加进去;
  • 额外的查询操作,已经查询性能,就跟Map<String, Integer> 一样:
    • count(Object) 返回某个元素出现的总次数。对于HashMultiset,count的时间复杂度为O(1);对于TreeMultiset,count的时间复杂度是O(logn);
    • entrySet() 返回Set<Multiset.entry<E>> ,工作机制类似于Map的entrySet;
    • elementSet() 返回不同元素的集合列表,类似于Map的keySet();
    • Multiset 实现的内存消耗是和不同元素的数量是成线性相关的;
    • setCount(E, int) 设置某个元素,出现的次数
    • remove(E, int) 将某个元素减少指定次数;
Multiset并不是一个Map

注意,Multiset并不是一个Map<E,Integer>,尽管它是Multiset的一部分。Multiset真的是Collection类型。其它值得注意的有:

  • Multiset仅仅拥有正数的count。没有元素的count值是负数。并且计数为0的元素,是不在Multiset中,它不会出现在elementSet() 和entrySet() 中;
  • multiset.size() 返回的是集合的大小。每次执行add操作,size都会加1;
  • multiset.iterator() 迭代的是每个元素的所有次数。也就是迭代的次数等于size;
  • Multiset 支持添加元素、移除元素,直接设置元素的个数。setCount(element, 0) 等于把一个元素出现的所有次数移除;
  • multiset.count(E) 对于不在Multiset中的元素,总是返回0;
Multiset的实现
mapMultiset是否支持null
HashMapHashMultisetYES
TreeMapTreeMultisetYES
LinkedHashMapLinkedHashMultisetYES
ConcurrentHashMapConcurrentHashMultisetNO
ImmutableMapImmutableMultisetNO
SortedMultiset

SortedMultiset 接口是Multiset的变体。它能有效的获取在特定范围的子集。例如:

SortedMultiset<Comparable<Integer>> result = set.subMultiset(3, BoundType.OPEN, 9, BoundType.CLOSED);

TreeMultiset 实现了 SortedMultiset接口。

(2)Multimap
Multimap介绍

Guava的Multimap很容见处理key到多个value的映射。

有两种思路理解Multimap概念:

  • 它是一个集合:里面是单个key到单个value到映射;
  • 作为key到多个value到映射;

一帮情况下,我们最好把Multimap接口作为第一视图,也可以用另一种视角,通过调用asMap()方法得到Map<K, Collection<V>> 。

非常重要的是,在Multimap中没有key会对应一个空集合。也就是说,一个key至少对应一个值,或者key不存在Multimap中。

很少直接使用Multimap接口,经常使用ListMultimap或SetMultimap,它们的key分别对应List和Set。

创建Multimap

创建Multimap最简单直接的方式是通过MultimapBuilder来创建:

ListMultimap<String, Integer> listMultimap =
                MultimapBuilder.hashKeys().arrayListValues().build();

也可以直接在实现类上使用create() 方法:

ArrayListMultimap<String, Integer> multimap = ArrayListMultimap.create();
Multimap 的修改
  • Multimap.get(key) 通过这个方法返回指定key关联的values;

修改Multimap底层的内容:

        ListMultimap<String, Integer> listMultimap =
                MultimapBuilder.hashKeys().arrayListValues().build();
        List<Integer> nums = listMultimap.get("name");
        System.out.println(nums);  // []
        nums.clear();
        nums.add(23);
        nums.add(21);
        System.out.println(listMultimap.get("name")); // [23, 21]

其它修改Multimap底层的方法:

  • put(K, V) 添加一个K 及相关的V;等价于multimap.get(K).add(V)
  • putAll(K, Iterable<V>) 添加K到每一个V的联系;等价于Iterables.addAll(multimap.get(K), Iterable<V>)
  • remove(K, V) 移除一个K到V的关系,如果Multimap改变了,返回true;等价于multimap.get(K).remove(V)
  • removeAll(K) 移除所有与K有关的关系,并返回合适的集合类型;等价于multimap.get(K).clear()
  • replaceValues(K, Iterable<V>) 清空现有的key相关的关系,并建立新的关系,返回原来与Key相关联的value集合;等价于multimap.get(K).clear(); Iterables.addAll(multimap.get(K), Iterable<V>)
Multimap支持多种强大的视图
  • asMap 将Multimap作为Map<K, Collection<V>>。返回的Map支持remove,也支持改变写入的集合。但是map不支持put或putAll。
  • entries 返回Collection<Map.Entry<K, V>>所有Multimap的关系
  • keySet 返回所有的key集合;
  • keys 返回Multiset。可以移除元素,但是不能添加;
  • values 返回所有的values。类似于Iterables.concat(listMultimap.asMap().values())
Multimap并不是一个Map

一个Multimap<K, V> 并不是一个Map<K, Collection<V>> ,尽管这样的Map能够用Multimap来实现。值得注意的不同如下:

  • Multimap.get(K) 总是返回一个非null,可能为空的集合。返回的集合允许你添加与key有关系的value;
  • 如果你想让K不在Multimap中的时候,调用get方法返回null。那么先使用asMap() 方法得到Map<K, Collection<V>> 视图。也可以使用Multimaps的静态方法asMap;
  • Multimap.containsKey(K) 只有当存在K的关系,才会返回true。
  • Multimap.entries() 返回所有key与value的关系对;如果要获取key-collection,使用Multimap.asMap().entrySet()
  • Multimap.size() 将返回所有Key的关系对数量,而不是不同key对个数。使用Multimap.asMap().size() 可以获取不同key对个数;
Multimap的实现

Multimap提供了很多实现,首先创建Multimap实现的方式是通过MultimapBuilder。

实现Key的行为像value的行为像
ArrayListMultimapHashMapArrayList
HashMultimapHashMapHashSet
LinkedListMultimapLinkedHashMapLinkedList
LinkedHashMultimapLinkedHashMapLinkedHashSet
TreeMultimapTreeMapTreeSet
ImmutableListMultimapImmutableMapImmutableList
ImmutableSetMultimapImmutableMapImmutableSet

这些实现中,除了不可变集合,其它的key和value都可以为空。

可以自定义自己的实现:Multimaps.newMultimap(Map, Supplier<Collection>)

(3)Bimap
Bimap介绍

把values转成keys的传统方式是维护两个独立的map,并同步更新它们。但是这是易于出错的。当map中已经存在value的时候会造成混乱,例如:

        Map<String, Integer> nameToId = Maps.newHashMap();
        Map<Integer, String> idToName = Maps.newHashMap();
        nameToId.put("Bob", 42);
        idToName.put(42, "Bob");
        // 如果 Bob 或 42 已经存在了,会怎么样?
        // 诡异的bug将会出现,如果我们忘了同步更新

一个Bimap<K, V> 是一个Map<K, V>:

  • 允许我们将Bimap<K, V> 反转,通过调用inverse() 方法;
  • 确保values是唯一的,让values() 作为一个set集合;

如果你试图通过Bimap.put(key, value)映射一个key到一个已经存在的value值上,会抛出异常:

        BiMap<String, Integer> biMap = HashBiMap.create();
        biMap.put("Bob", 42);
        biMap.put("Job", 42);
/*
Exception in thread "main" java.lang.IllegalArgumentException: value already present: 42
	at com.google.common.collect.HashBiMap.put(HashBiMap.java:310)
	at com.google.common.collect.HashBiMap.put(HashBiMap.java:290)
	at com.hef.guava.collection.GuavaCollectionDemo.biMapTest(GuavaCollectionDemo.java:32)
	at com.hef.guava.collection.GuavaCollectionDemo.main(GuavaCollectionDemo.java:19)
*/

如果你想删除预先已经存在的值,可以通过bimap.forcePut(key, value):

        BiMap<String, Integer> biMap = HashBiMap.create();
        biMap.put("Bob", 42);
        biMap.forcePut("Job", 42);
        System.out.println(biMap); // {Job=42}
Bimap的具体实现
key-value map双向map
HashMapHashBimap
ImmutableMapImmutableBimap
EnumMapEnumBimap
EnumMapEnumHashBimap
(4)Table
Table的使用

类似一个二维表,由“行、列、值”三元素组成。这里的行和列,可以是任意类型。

通常,当你想要一次索引多个keys时,你可能会想起使用Map<FirstName,Map<LastName,Person>>。Guava提供了一种新的集合类型Table,它支持使用任何的行列,以展示不同的视图:

  • rowMap() 对于Table<R, C, V>,将返回Map<R, Map<C, V>>。
  • 同样的rowKeySet() 将返回Set<R>;
  • row(r) 返回非null的Map<C, V>,向这个返回值的Map中写数据,值将写入底层的Table中;
  • 类似的类方法被提供:columnMap()、columnKeySet()、column(c)
  • cellSet() 将返回Set<Table.Cell<R, C, V>> , 类似于Map.Entry;
Table的实现
  • HashBasedTable, 它本质上是基于HashMap<R, HashMap<C, V>>;
  • TreeBasedTable, 它本质上是基于TreeMap<R, TreeMap<C, V>>;
  • ImmutableTable
  • ArrayTable 需要在创建的时候指定行和列。能够提升速度和内存使用率;
(5)ClassToInstanceMap

有些使用想将不同的类型作为key,该类型的值作为value。就可以使用ClassToInstanceMap。从技术上说ClassToInstanceMap实现了Map<Class<? extends B>, B>,换句话说,它是一个B类型到B类型值的映射。

ClassToInstanceMap有一个带有一个参数的构造,通过这个参数可以确定ClassToInstanceMap中B的上界类型。

除了扩展Map接口,ClassToInstanceMap还提供了两个方法:

  • T getInstance(Class<T>) 获取某个类型的值;
  • putInstance(Class<T>, T) 设置某个类型的值;

Guava提供了两个有用的ClassToInstanceMap实现:

  • MutableClassToInstanceMap
  • ImmutableClassToInstanceMap
(6)RangeSet
RangeSet 介绍

RangeSet描述了一个没有关联的非空的范围。当添加一个范围到可变集合RangeSet到时候,任何有联系的范围将合并在一块,空的范围将被忽略。例如:

        TreeRangeSet<Integer> rangeSet = TreeRangeSet.create();
        rangeSet.add(Range.closed(1, 10));
        System.out.println(rangeSet);  // [[1..10]]
        rangeSet.add(Range.closedOpen(11, 15));
        System.out.println(rangeSet);  // [[1..10], [11..15)]
        rangeSet.add(Range.closedOpen(15, 20));
        System.out.println(rangeSet);// [[1..10], [11..20)]
        rangeSet.add(Range.openClosed(0, 0));
        System.out.println(rangeSet); // [[1..10], [11..20)]
        rangeSet.remove(Range.open(5, 10));
        System.out.println(rangeSet); // [[1..5], [10..10], [11..20)]
RangeSet视图
  • complement() 展示rangeSet的补集

            TreeRangeSet<Integer> rangeSet = TreeRangeSet.create();
            rangeSet.add(Range.openClosed(2, 5));
            RangeSet<Integer> complement = rangeSet.complement();
            System.out.println(rangeSet);  // [(2..5]]
            System.out.println(complement); // [(-∞..2], (5..+∞)]
    
  • subRangeSet(Range) 获取特定范围内的子集

    RangeSet<Integer> subRangeSet = rangeSet.subRangeSet(Range.closed(4, 5));
    
  • asRanges() 返回一个Set<Range> ,用于迭代所有子集

  • asSet() 仅用于不可变集合。

查询操作
  • contains(C) 查询一个元素是否在RangSet的范围中
  • rangeContaining(C) 返回包含某一个元素的范围
  • encloses(Range) 测试一个范围是否在RangeSet中
  • span() 返回包含RangeSet所有范围的最小范围
(7)RangeMap
RangeMap 介绍

范围到值的映射。RangeMap 不会合并两个相同的范围,即便两个相邻的范围映射到同一个值。

        TreeRangeMap<Integer, String> rangeMap = TreeRangeMap.create();
        rangeMap.put(Range.closed(1, 10), "foo");
        System.out.println(rangeMap); // [[1..10]=foo]
        rangeMap.put(Range.open(3, 6), "bar");
        System.out.println(rangeMap); // [[1..3]=foo, (3..6)=bar, [6..10]=foo]
        rangeMap.put(Range.open(10, 20), "foo");
        System.out.println(rangeMap); // [[1..3]=foo, (3..6)=bar, [6..10]=foo, (10..20)=foo]
        rangeMap.remove(Range.closed(5, 11));
        System.out.println(rangeMap); // [[1..3]=foo, (3..5)=bar, (11..20)=foo]
视图
  • asMapOfRanges 返回Map<Range, V> 用于遍历RangeMap
  • subRangeMap(Range) 返回子的RangeMap

3.3 强大的集合工具

Guava提供了很多工具。

(1)集合工具类(辅助类)与接口的对应
接口JDK还是Guava对应Guava的工具类
CollectionJDKCollections2
ListJDKLists
SetJDKSets
SortedSetJDKSets
MapJDKMaps
SortedMapJDKMaps
QueueJDKQueues
MultisetGuavaMultisets
MultimapGuavaMultimaps
BiMapGuavaMaps
TableGuavaTables
(2)静态的方式构造集合对象

在JDK7之前,构建一个泛型集合需要写令人不愉快的代码:

List<TypeThatsTooLongForItsOwnGood> list = new ArrayList<TypeThatsTooLongForItsOwnGood>();

JDK7 的菱形运算符减少了这方面的麻烦:

List<TypeThatsTooLongForItsOwnGood> list = new ArrayList<>();

Guava 提供了静态方法,通过泛形推断右侧的类型:

ArrayList<TypeThatsTooLongForItsOwnGood> list02 = Lists.newArrayList();

在创建集合的时候,可以非常方便的初始化集合元素:

ArrayList<String> list03 = Lists.newArrayList("aa", "bb", "cc");

通过静态工厂方法,初始化集合的大小:

// 明确值
ArrayList<String> list04 = Lists.newArrayListWithCapacity(10);
// 估计值
ArrayList<String> list05 = Lists.newArrayListWithExpectedSize(10);
HashSet<Integer> set = Sets.newHashSetWithExpectedSize(10);
(3)Iterables
Iterables 的特点:惰性

只要有可能,Guava更喜欢提供接受Iterable而不是Collection的程序。在Google里,一个集合实际上并没有存在主存中的现象是不稀奇的,这个集合来自数据库或来自其他的数据中心。因为没有抓取到所有元素,所以并不支持size() 操作。

因此,你希望看到的所有集合操作都能在Iterables中找到。Iterables的大多方法都支持接受一个Iterator的版本。

在Iterables类中它的绝大多数方法都是懒惰的:只有当绝对需要迭代的时候,迭代操作发生。返回Iterables的方法,返回的是一个懒惰的计算视图,而不是构建一个明确的集合。

Iterables常用的静态方法
  • concat(Iterable...) 连接多个Iterables,返回一个惰性视图;

  • frequency 返回元素在集合中出现的频次

    对比Collections.frequency(Collection, o) 。Multiset

  • partition(Iterable, int) 返回一个不可修改的Iterable,里面是分成指定大小的块。

  • paddedPartition(Iterable, int) 分成指定大小的块,如果某一块的元素个数不足,用null补充。

  • getFirst(Iterable, T default) 返回第一个元素,没有Iterable为空,返回默认值;

  • getLast(Iterable) 返回最后一个元素,如果为空抛出异常

  • getLast(Iterable, T default) 返回最后一个元素,如果没有返回默认值

  • elementsEqual(Iterable, Iterable) 如果两个Iterable有相同的元素,则返回true

  • unmodifiableIterable(Iterable) 返回一个不可修改的视图

  • limit(Iterable, int) 返回集合的前几个元素

  • getOnlyElement(Iterable) 返回Iterable中仅有的一个元素。如果Iterable中有多个元素,将会报错

  • getOnlyElement(Iterable, T default) 返回Iterable中仅有的一个元素,如果Iterable为空,返回默认值

类集合操作

一些方法在Iterable中不支持,但在Collection中支持。当向Iterables中下面的这些方法传递Collection的时候,实际的操作会调用Collection接口的方法:

  • addAll(Collection, Iterable)

  • contains(Iterable, o)

  • removeAll(Iterable, Collection)

  • retainAll(Iterable removeFrom, Collection retain)

  • size(Iterable)

  • toArray(Iterable, Class<T>)

  • isEmpty(Iterable)

  • get(Iterable, int) 类似List.get(i)

  • toString(Iterable)

(4)Lists
  • partition(List, int) 将list切分成若干个大小为size的小块。
  • reverse(List) 反转list中的元素

静态工厂方法:

  • Lists.newLinkedList()
  • Lists.newArrayList()
(5)比较

查找最小值、最大值。一个看似很简单,却因为考虑最小的分配、装箱、API的灵活性而变得复杂。

要对比的元素刚好两个元素超过两个元素
不需要包装箱的基本数据类型Math(a, b)Ints.max(a, b, c);
Comparable类型的实例Comparators.max(a, b)Collections.max(Arrays.asList(a, b, c))
使用自定义的ComparatorComparators.max(a, b, comparator)Collections.max(Arrays.asList(a, b, c), comparator)
        Comparators.max("aa", "bb", Comparator.comparingInt(String::length));
        Collections.max(Arrays.asList("aa", "bb"),Comparator.comparingInt(String::length));
(6)Sets
SetView
  • union(set, set) 获取两个set的并集
  • intersection(set, set) 获取两个set的交集
  • difference(Set, Set) 返回前一个集合与后一个集合不同的元素
  • symmetricDifference(Set, Set) 差集,除去两个集合共有的元素
        HashSet<String> set01 = Sets.newHashSet("aa", "bb", "cc");
        HashSet<String> set02 = Sets.newHashSet("ee", "bb", "ff");
        Sets.SetView<String> result = Sets.union(set01, set02);
        System.out.println(result);
        Sets.SetView<String> intersection = Sets.intersection(set01, set02);
        System.out.println(intersection);
        Sets.SetView<String> difference = Sets.difference(set01, set02);
        System.out.println(difference);
        Sets.SetView<String> difference1 = Sets.symmetricDifference(set01, set02);
        System.out.println(difference1);

这些操作返回的是SetView:

  • 可以直接把SetView当作Set使用,因为它实现了Set接口;
  • copyInto(Set) 将SetView中的元素拷贝到一个Set中;
  • immutableCopy() 拷贝成一个不可变集合
其它的操作
  • cartesianProduct(Set...) 多个集合进行组合,形成笛卡尔积
  • Sets.powerSet(Set) 获取元素的子集
HashSet<String> set01 = Sets.newHashSet("aa", "bb", "cc");
Set<Set<String>> sets = Sets.powerSet(set01);
// {{}, {"aa"}, {"bb"}, {"cc"}, {"aa", "bb"}, {"aa", "cc"}, {"bb", "cc"}, {"aa", "bb", "cc"}}
静态工厂
  • newHashSet()
  • newTreeSet()
  • newLinkedHashSet()
(7)Maps

Maps中有很多酷的方法,值得分别解释:

Maps.uniqueIndex(Iterable, Function)

解决有一堆对象(Iterable),这一些对象都有唯一的属性。想要根据属性查到对象。

// 假设这些字符串的长度是唯一的,想要根据长度查找相应的字符串
// 一旦这个唯一的属性不唯一就会报错
ArrayList<String> list = Lists.newArrayList("a", "bb", "ccc", "dddd", "eeeee", "ffffff");
ImmutableMap<Integer, String> lenMap = Maps.uniqueIndex(list, String::length);
String item = lenMap.get(5);
System.out.println(item);
Maps.difference(Map, Map)

根据MapDifference的方法:

  • entriesInCommon() key和value都匹配的项目
  • entriesDiffering() key相同,value不相同的项。返回值是Map<K, MapDifference.ValueDifference<V>> 通过MapDifference.ValueDifference能看到左右两边的值;
  • entriesOnlyOnLeft() 返回key只在左边出现的元素对;
  • entriesOnlyOnRight() 返回key只在右边出现的元素对;
        Map<String, Integer> map01 = ImmutableMap.of("aa", 1, "bb", 2, "cc", 3);
        Map<String, Integer> map02 = ImmutableMap.of("aa", 2, "d", 2, "cc", 3);
        MapDifference<String, Integer> difference = Maps.difference(map01, map02);
        Map<String, Integer> entriesInCommon = difference.entriesInCommon();
        System.out.println(entriesInCommon);  // {cc=3}
        Map<String, MapDifference.ValueDifference<Integer>> entriesDiffering = difference.entriesDiffering();
        System.out.println(entriesDiffering); // {aa=(1, 2)}
        Map<String, Integer> entriesOnlyOnLeft = difference.entriesOnlyOnLeft();
        System.out.println(entriesOnlyOnLeft); // {bb=2}
        Map<String, Integer> entriesOnlyOnRight = difference.entriesOnlyOnRight();
        System.out.println(entriesOnlyOnRight); // {d=2}
BiMap工具

因为BiMap也是一个Map,所以它的工具方法也在Maps中:

  • Maps.synchronizedBiMap(BiMap) 等价的方法Collections.synchronizedMap(Map);
  • Maps.unmodifiableBiMap(BiMap) 等价的方法Collections.unmodifiableMap(Map);
静态工厂
  • Maps.newHashMap()
  • Maps.newLinkedHashMap()
  • Maps.newTreeMap()
  • Maps.newEnumMap()
  • Maps.newConcurrentMap()
  • Maps.newIdentityHashMap()
(8)Multisets

标准的Collection操作,例如containsAll,忽略了元素在Multiset的个数,仅关心元素是否在Multiset中。Multisets提供了很多方法考虑多重性操作:

  • containsOccurrences(superMultiset, subMultiset) 如果subMultiset中的所有元素都有subMultiset.count(o)<=superMultiset(o) ,则返回true。
  • removeOccurrences(superMultiset, subMultiset) 根据subMultiset中数量移除superMultise的元素。
  • retainOccurrences(superMultiset, subMultiset) 根据subMultiset中的元素个数,保留superMultiset中的元素,其余的都移除
  • intersection(superMultiset, subMultiset) 根据元素个数获取交集
        HashMultiset<String> multiset = HashMultiset.create();
        HashMultiset<String> multiset02 = HashMultiset.create();
        multiset.addAll(Arrays.asList("aa", "bb", "aa", "bb", "cc", "cc"));
        multiset02.addAll(Arrays.asList("aa", "bb", "aa", "bb", "cc", "dd"));
				Multiset<String> intersection = Multisets.intersection(multiset, multiset02);
        System.out.println(intersection); // [aa x 2, bb x 2, cc]

Multisets中的其他工具方法:

  • copyHighestCountFirst(multiset) 返回一个不可变的Multiset,并且迭代的时候元素频次最高的在前面;
  • unmodifiableMultiset(multiset) 返回一个不可修改的Multiset
  • unmodifiableSortedMultiset(SortedMultiset) 返回一个有序的不可修改的Multiset
(9)Multimaps

Multimap提供了很多常用的方法,下面分开解释:

index(Iterable, Function),返回一个不可变集合

和Maps.uniqueIndex 功能类似。Multimaps.index(Iterable, Function) 是把一批对象,属性相同的分成一组。可以根据属性找到一批对象。

        ImmutableListMultimap<Integer, String> multimap1 = Multimaps.index(Lists.newArrayList("aa", "bb", "cc", "dd", "world"), String::length);
        System.out.println(multimap1);// {2=[aa, bb, cc, dd], 5=[world]}
invertFrom(Multimap toInvert, Multimap dest)

因为多个key可以对应一个value,多个value可以对应一个key

        ListMultimap<String, Integer> multimap = MultimapBuilder.hashKeys().arrayListValues().build();
        multimap.put("name", 23);
        multimap.put("name", 21);
        multimap.put("value", 21);
        System.out.println(multimap); // {name=[23, 21], value=[21]}
        ListMultimap<Integer, String> multimap2 = Multimaps.invertFrom(multimap, MultimapBuilder.hashKeys().arrayListValues().build());
        System.out.println(multimap2);  // {21=[name, value], 23=[name]}

对于ImmutableMultimap,可以直接调用invert() 方法。

forMap(Map) 将一个Map转成SetMultimap
        Map<String, Integer> map = ImmutableMap.of("aa", 1, "bb", 1, "cc", 3);
        System.out.println(map);  // {aa=1, bb=1, cc=3}
        SetMultimap<String, Integer> setMultimap = Multimaps.forMap(map);
        System.out.println(setMultimap); // {aa=[1], bb=[1], cc=[3]}
        HashMultimap<Integer, String> multimap3 = Multimaps.invertFrom(setMultimap, HashMultimap.<Integer, String>create());
        System.out.println(multimap3);// {1=[aa, bb], 3=[cc]}
包装器:Multimaps提供了传统的包装方法
Multimap类型不可修改类型同步类型自定义类型
MultimapunmodifiableMultimapsynchronizedMultimapnewMultimap
ListMultimapunmodifiableListMultimapsynchronizedListMultimapnewListMultimap
SetMultimapunmodifiableSetMultimapsynchronizedSetMultimapnewSetMultimap
SortedSetMultimapunmodifiableSortedSetMultimapsynchronizedSortedSetMultimapnewSortedSetMultimap

自定义类型,允许指定一个实现,能够在返回的Multimap中使用。

自定义类型需要提供一个Supplier方法,用于创建新的集合。

Multimap<String, Integer> multimap4 = 
  Multimaps.newMultimap(new HashMap<String, Collection<Integer>>(), Lists::newArrayList);
(10)Tables
customTable

对比newXXXMultimap(Map, Supplier) 。customTable 可以指定row、col的数据结构。

Table<String, String, Integer> table = 
                Tables.<String, String, Integer>newCustomTable(new HashMap<String, Map<String, Integer>>(), 
                        LinkedHashMap::new);
transpose 将行和列到转
        Table<String, String, Integer> table =
                Tables.<String, String, Integer>newCustomTable(new HashMap<String, Map<String, Integer>>(),
                        LinkedHashMap::new);
        table.put("name", "value", 2);
        System.out.println(table);
        Table<String, String, Integer> transpose =
                Tables.transpose(table);
        System.out.println(transpose);
包装Table
  • unmodifiableTable 转成不可修改的Table

    Table<String, String, Integer> unmodifiableTable = Tables.unmodifiableTable(table);
    
  • unmodifiableRowSortedTable 转成row排序的Table

    RowSortedTable<String, String, Integer> treeBasedTable = TreeBasedTable.create();
            treeBasedTable.put("name1", "name2", 23);
            RowSortedTable<String, String, Integer> unmodifiableRowSortedTable = Tables.unmodifiableRowSortedTable(treeBasedTable);
    

3.4 扩展工具

有些时候你需要写自己的集合扩展。或者当元素添加到集合汇总时,你想添加特定的行为;或者你想编写一个数据库支持的Iterable。Guava提供了很多工具让你做这些事情更容易。

(1)Forwarding 装饰器

对于各种集合接口。Guava提供了Forwarding抽象类简化了装饰器模式的使用。

Forwarding类定义了一个抽象方法delegate(),你应该复写它并返回被装饰的对象。其他的方法就简单的委托给装饰者。例如,ForwardingList.get(i) 是简单的实现了delegate().get(i)

(2)PeekingIterator

通过Iterators.peekingIterator(Iteraotr) 得到PeekingIterator。其中的peek() 能够得到next() 元素

        List<String> result = Lists.newArrayList("a", "b", "c");
        PeekingIterator<String> peekingIterator = Iterators.peekingIterator(result.iterator());
        // 能够打印5次 a
        for (int i = 0; i < 5; i++) {
            System.out.println(peekingIterator.peek());
        }
(3)AbstractIterator

通过AbstractIterator 可以很方便的实现自己的Iterator:

    private Iterator<String> skipNullIterator(final Iterator<String> in){
        return new AbstractIterator<String>() {
            @CheckForNull
            @Override
            protected String computeNext() {
                while (in.hasNext()) {
                    String s = in.next();
                    if (s!=null) {
                        return s;
                    }
                }
                return endOfData();
            }
        };
    }
(4)AbstractSequentialIterator
    private Iterator<Integer> sequentialIterator() {
        return new AbstractSequentialIterator<Integer>(2) {
            @CheckForNull
            @Override
            protected Integer computeNext(Integer previous) {
                return previous>100?null:previous*3;
            }
        };
    }
  • 必须要有一个初始值;
  • null作为迭代的结束;
  • AbstractSequentialIterator不能用于迭代元素包含null的实现;
详解Java Google Guava Guava是Java开发中非常有价值的工具库,它通过提供高级数据结构、并发工具、实用工具类等,极大地提高了开发效率和代码质量。然而,开发者在享受其便利的同时,也应注意其学习成本、版本兼容性和性能优化等问题,通过合理的使用策略和持续的学习,最大化Guava的效用。 阅读详情

相关推荐

Java限流之 —— Guawa

前言 在上一篇,我们聊了下限流相关的概念和常用的限流解决方案,本篇我们先从最简单的限流组件Guawa开始 关于Guawa的介绍,我们上一篇有过简单的介绍,这里不再过多说明,直接从撸码开始吧 1、pom导入guawa依赖包 <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId>

congge 1万+

Guava简介(详细)

Guava简介(详细、全)

weixin_42326851的博客 2万+

西门子S7-1200和IFM IO-Link主站连接与设定

硬件配置:S7-1200 1214DC/DC/DC,IFM AL1102支持Profinet,IFM支持IO-Link的流量传感器SBG233,流量开关SI5010,接近开关IF6123。第三步:打开IFM moneo configure可查看到流量传感器SBG233 PLC映射所有输出共占用4个byte,也可在IFM官网下载传感器对应IODD文件。第五步:组态编程编译下载。最近新做了一个项目,用到了IO-Link设备,今天来分享一下S7-1200和IFM IO-Link主站模块AL1102的连接过程。

GreatShen168的博客 6492

Guava官方文档中文版(一)-介绍基本工具

Guava文档中文版

dzq584462393的专栏 6366

车载监控系统升级指南:如何用XS9950A替换TP9950实现高清视频解码(附配置代码)

本文提供了从TP9950升级至XS9950A车载视频解码芯片的完整实战指南。详细解析了硬件设计调整、PCB布局要点,并重点分享了通过I2C配置寄存器实现高清HDCCTV视频解码与同轴音频集成的软件驱动代码与调试技巧,助力工程师平滑完成系统升级。

weixin_29268637的博客 364

WinForm实战:如何用C#和PdfiumViewer打造一个功能齐全的PDF阅读器(含源码)

本文详细介绍了如何使用C#和PdfiumViewer库从零开始构建一个功能齐全的WinForm PDF阅读器。内容涵盖项目环境搭建、核心组件解析、基于命令模式的工具栏与UI架构设计,并深入讲解了智能缩放、页面导航、打印功能等核心交互的实现,以及内存管理和性能优化等高级话题,为开发者提供了一套完整的实战解决方案。

docker8compose的博客 503

Guava中的使用

Guava的使用:基础功能、集合、缓存、EventBus、反射、并发、字符串功能

hefrankeleyn的博客 1097

dummy机械臂代码学习(六)

CLI-Tool文件夹下又包含四个子文件夹,以及一个gitignore文件、一个run_shell.py的python文件、一个run.bat文件和一个zadig exe文件。库解析命令行参数,并根据不同的命令执行相应的操作。Zadig是一个用于Windows操作系统的开源工具,它的主要功能是安装和配置USB驱动程序。的目的呢就是确保在版本控制过程中,不必要的或自动生成的文件被忽略掉,来保持代码仓库的干净和整洁。,DummyStudio的软件作者已放在GitHub,我们主要看CLI-Tool文件夹就行。

jumaohhh的博客 1432

Guava介绍

https://code.google.com/p/guava-libraries/wiki/GuavaExplained?tm=6 User Guide 用户指南 The Guava project contains several ofGoogle's core libraries that we rely on in our Java-based project

hyl963254113的专栏 728

域控制器迁移后NETLOGON共享丢失的修复指南

本文详细解析了在Windows域控制器迁移后NETLOGON共享丢失的故障现象与修复方法。通过使用Dcdiag工具诊断,定位注册表键值SysVolReady异常及SCRIPTS目录缺失等核心原因,并提供了修改注册表、创建目录、重启Netlogon服务等逐步修复指南,帮助管理员快速恢复域用户登录功能,确保业务连续性。

weixin_29245767的博客 88

D19: OpenClaw 安全性配置指南

在 OpenClaw 的 30 天探索旅程中,我们已经探讨了邮件自动化(D18)。今天进入安全专题,我将分享一个至关重要的话题:OpenClaw 安全性配置指南。 随着 OpenClaw 深入工作流核心,它掌握了越来越多的敏感信息: API Keys(GitHub、飞书、Notion、Jira...) 企业数据(代码库、文档、聊天记录) 个人数据(日历、邮件、任务) 系统权限(文件读写、命令执行、网络访问) 安全不是可选项,而是必选项。一次配置失误可能导致: API Key 泄露 → 账号被盗用权限

刘一说的IT专栏 203

[AIGC] Guava Library 中常用的一些工具类详解

是一个功能强大的 Java 库,它提供了大量用于优化代码和提高代码质量的工具。该库提供了许多常用的实用类,下面我们来详细介绍一些在编程应用开发中经常需要的。

程序员三木的博客 1562

C#上位机面试必问30题:机器视觉+工业通信+性能优化(含答案解析)

工业通信:围绕“稳定性”展开,重点答异常处理、断线重连、资源释放;机器视觉:围绕“精度+速度”展开,重点答预处理、ROI、参数优化;性能优化:围绕“UI不卡、内存不泄、效率不低”展开,重点答异步、限流、资源复用;面试加分项:结合实际项目案例(如“我在XX项目中用信号量解决了多设备并发卡顿问题”)。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 1509

Google Guava,牛逼的脚手架

01、前世今生你好呀,我是 Guava。1995 年的时候,我的“公明”哥哥——Java 出生了。经过 20 年的发展,他已经成为世界上最流行的编程语言了,请允许我有失公允的把“之一”给去...

磊哥聊Java 864

Android Framework WMS全面概述和知识要点

WindowManagerService 类是整个 WMS 的核心大脑,处于系统服务层,负责协调和管理所有与窗口相关的操作。它在 Android 系统架构中起到承上启下的关键作用,向上对接应用层的窗口请求,向下与 SurfaceFlinger 等底层组件交互,确保窗口能够正确地在屏幕上显示和管理。

大模型大数据攻城狮的专栏 1793

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Google Guava v07范例

我们在TouK举办了一个名为“每周技术研讨会”的活动,即每个星期五的16:00,每个愿意参加的人都有一个演讲。 我们展示了我们在家学习和学习的东西,但是我们也设有一个公告板,上面有人们想听的话题。 上周MaciejPróchniak谈论了Clojure ,这次有几个人要求介绍Google Guava库 。 由于这是一项艰巨而简单的任务,所以我很乐意交付。 WTF是番石榴吗? 这是一...

最佳 Java 编程 210

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Google Guava 类库 全面解析

Google Guava 是 Google 公司开源的 Java 核心库,提供了很多实用的工具类和方法,用于简化 Java 编程中的常见任务。它是在 Google 内部广泛使用的 Java 库的基础上开源出来的,经过了大规模生产环境的验证。javaif (!Google Guava 提供了丰富的工具类和方法,大大简化了 Java 开发中的常见任务。代码质量提升:通过提供经过验证的工具类,减少自定义代码的错误开发效率提高:简洁的 API 设计减少了样板代码性能优化:经过精心优化的实现更好的实践。

油墨香^-^的博客 940

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

沙丁解牛之 Google Guava - 简介

Guava项目是Google Java工程的核心基础类库,主要包括:集合,缓存,原始类型扩展,并发,注解,字符串处理,IO等等。谷人希不愧是人类的希望,Guava不大五脏俱全,里面有很多令人赞叹的代码,读后有种原来Java也可以这么优雅的感觉。现在我做项目Apache的一些包可以不要,但是Guava和Junit必不可少。 Above All,建立maven java工程,将Guava包和Ju...

buru 208

guava 学习笔记(二) 瓜娃(guava)的API快速熟悉使用

1,大纲 让我们来熟悉瓜娃,并体验下它的一些API,分成如下几个部分: Introduction Guava Collection API Guava BasicUtilities IO API Cache API 2,为神马选择瓜娃? 瓜娃是java API蛋糕上的冰激凌(精华) 高效设计良好的API. 被google的开发者设计,实现和使用。 遵循高效的java这本书的好的语法实践。 使代码更刻度,简洁,简单。 使用java 1.5的特性, 流行的API,动态的开发 它提供.

u014089832的博客 319

Google Guava常用工具类入门使用介绍及样例说明

目录概述基础工具1.Optional2.Objects.equals3.Objects.hash4.MoreObjects.toStringHelper5.ComparisonChain6.Preconditions集合工具1.ImmutableSet2.ImmutableList3.4.5.6.字符串工具1.2.3.4.5.6.附录1.Person.java 概述 工具类将经常用的一些方法进行了封装,遇到相同的场景时不需要重复造轮子,从而大量节省开发人员时间和提高工作效率,Guava就是谷歌发布的高效率的

进修的CODER 3348

告别复杂PDF处理:Guava库的媒体类型与文件操作实用指南

你是否还在为Java项目中的PDF文件处理而烦恼?面对各种文档类型判断、文件内容处理时总是要编写大量重复代码?本文将带你发现Google Guava库在文件处理领域的隐藏实力,通过简单实用的工具类解决日常开发中的文档处理痛点,让你不再为基础功能编写重复代码。 ## Guava与PDF:媒体类型常量的妙用 在处理PDF文件时,我们经常需要指定正确的媒体类型(MIME类型)。Guava库的`Med...

gitblog_00565的博客 1077

Guava学习笔记目录

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_33923148的博客 161

Guava使用案例

Guava使用案例介绍Guava是一种基于开源的Java库,谷歌很多项目使用它的很多核心库。这个库是为了方便编码,并减少编码错误。本篇文章主要以测试实例来讲解Guava比较常用的一些类和功能

yowasa的博客 5648

探索 Google Guava

Google Guava 是一个功能强大且广泛使用的 Java 核心库,它通过提供大量的实用工具类和方法,显著提高了 Java 程序的性能、可靠性和可维护性。从基础的使用场景如集合操作和缓存管理,到高级特性如并发编程和字符串处理,Guava 都为开发者提供了极大的便利。:Guava 通过提供大量的实用方法和类,减少了样板代码的数量,使得 Java 程序的编写更加简洁明了。:Guava 提供的许多工具类都经过了精心设计和优化,相比 Java 标准库中的等价实现,它们在性能上往往有更好的表现。

Aaron_945的博客 1339

Guava-使用向导

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34061555的博客 95
上一篇: 一张图了解Java8
下一篇: Guava-字符串工具
鲲鹏飞九万里
博客等级 码龄10年 177粉丝 350原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值