ZetCode

Google Guava 简介

最后修改于 2024 年 1 月 27 日

本教程是对 Guava 库的介绍。 我们将了解 Guava 库的一些有趣的功能。

Guava

Google Guava 是一个用于 Java 的开源通用库集合,主要由 Google 工程师开发。 Google 有许多 Java 项目。 Guava 解决了这些项目中遇到的许多常见问题,包括集合、数学、函数式编程、输入和输出以及字符串等领域。

Guava 的某些功能已经包含在 JDK 中; 例如,String.join 方法已引入 JDK 8。

Guava Maven 依赖

在我们的示例中,我们使用以下依赖项。

implementation 'com.google.guava:guava:31.1-jre'

Guava 初始化集合

Guava 允许在一行中初始化集合。

com/zetcode/InitializeCollectionEx.java
package com.zetcode;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import java.util.List;
import java.util.Map;

public class InitializeCollectionEx {

    public static void main(String[] args) {

        Map items = ImmutableMap.of("coin", 3, "glass", 4, "pencil", 1);

        items.entrySet()
                .stream()
                .forEach(System.out::println);
        
        List<String> fruits = Lists.newArrayList("orange", "banana", "kiwi", 
                "mandarin", "date", "quince");
        
        for (String fruit: fruits) {
            System.out.println(fruit);
        }
    }
}

在此示例中,我们使用 Guava 的工厂方法创建了一个映射和一个列表。

Map items = ImmutableMap.of("coin", 3, "glass", 4, "pencil", 1);

使用 ImmutableMap.of 方法创建一个新映射。

List<String> fruits = Lists.newArrayList("orange", "banana", "kiwi", 
        "mandarin", "date", "quince");

使用 Lists.newArrayList 方法创建一个新的字符串列表。

$ gradle run -q
coin=3
glass=4
pencil=1
orange
banana
kiwi
mandarin
date
quince

Guava FluentIterable

FluentIterable 提供了一个强大而简单的 API,用于以流畅的方式操作 Iterable 实例。 它允许我们以各种方式过滤和转换集合。

com/zetcode/FluentIterableEx.java
package com.zetcode;

import com.google.common.base.Functions;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Lists;
import java.util.List;

public class FluentIterableEx {

    public static void main(String[] args) {

        List<Car> cars = Lists.newArrayList(new Car(1, "Audi", 52642),
            new Car(2, "Mercedes", 57127), new Car(3, "Skoda", 9000),
            new Car(4, "Volvo", 29000));

        Predicate<Car> byPrice = car -> car.price() <= 30000;

        List<String> results = FluentIterable.from(cars)
                .filter(byPrice)
                .transform(Functions.toStringFunction())
                .toList();
        
        System.out.println(results);
    }
}

record Car(int id, String name, int price) {}

在代码示例中,我们有一个汽车对象列表。 我们通过将列表缩小到价格低于 30000 个单位的汽车来转换列表。

List<Car> cars = Lists.newArrayList(new Car(1, "Audi", 52642),
        new Car(2, "Mercedes", 57127), new Car(3, "Skoda", 9000),
        new Car(4, "Volvo", 29000));

创建了一个 Car 对象列表。 JDK 中没有集合字面量。 我们使用 Guava 中的 Lists.newArrayList 来初始化列表。

Predicate<Car> byPrice = car -> car.price() <= 30000;

创建了一个 Predicate。 谓词是一个返回布尔值的函数。 此谓词确定汽车是否低于 30000 美元。

List<String> results = FluentIterable.from(cars)
        .filter(byPrice)
        .transform(Functions.toStringFunction())
        .toList();

cars 集合创建一个 FluentIterable。 谓词函数应用于 FluentIterable。 检索到的元素被转换为元素列表; 这些元素是从 toString 函数返回的字符串。

$ gradle run -q
[Car[id=3, name=Skoda, price=9000], Car[id=4, name=Volvo, price=29000]]

Guava 谓词

一般来说,谓词是关于某件事的陈述,它要么是真,要么是假。

如果被测试的对象引用不是 null,则 Predicates.notNull 返回一个计算结果为 true 的谓词。

com/zetcode/PredicateEx.java
package com.zetcode;

import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import java.util.List;

public class PredicateEx {

    public static void main(String[] args) {

        List<Integer> values = Lists.newArrayList(3, null, 4, 7, 
                8, null, 7);
        
        Iterable<Integer> filtered = Iterables.filter(values, 
                Predicates.notNull());
        
        for (Integer i: filtered) {
            System.out.println(i);
        }
    }
}

在第一个示例中,我们使用谓词从集合中排除 null 值。

List<Integer> values = Lists.newArrayList(3, null, 4, 7, 
        8, null, 7);

使用 Guava 的 Lists.newArrayList,我们创建了一个 Integer 值列表。 该列表包含两个 null 值。

Iterable<Integer> filtered = Iterables.filter(values, 
        Predicates.notNull());

我们通过应用 Predicates.notNull 来过滤值。 Iterables.filter 返回一个可迭代对象。

for (Integer i: filtered) {
    System.out.println(i);
}

我们遍历过滤后的列表并打印其元素。

$ gradle run -q
3
4
7
8
7

第二个示例通过特定的文本模式过滤集合。 在编程中,谓词通常用于过滤数据。

com/zetcode/PredicateEx2.java
package com.zetcode;

import com.google.common.base.Predicates;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import java.util.Collection;
import java.util.List;

public class PredicateEx2 {

    public static void main(String[] args) {

        List<String> items = Lists.newArrayList("coin", "book",
                "cup", "purse", "bottle");
        Collection<String> result  = Collections2.filter(items, 
                Predicates.containsPattern("o"));
        
        for (String item: result) {
            System.out.println(item);
        }
    }
}

代码示例创建一个项目列表,稍后按特定模式过滤该列表。

Collection<String> result = Collections2.filter(items, 
        Predicates.containsPattern("o"));

Predicates.containsPattern 返回一个谓词,该谓词查找包含字符“o”的项目。 该谓词被传递给 Collections2.filter 方法。

$ gradle run -q
coin
book
bottle

使用 Guava 读取所有行

Files.readLines 允许一次性读取文件中的所有行。

resources/balzac.txt
Honoré de Balzac, original name Honoré Balzac (born May 20, 1799, Tours, 
France—died August 18, 1850, Paris) French literary artist who produced 
a vast number of novels and short stories collectively called 
La Comédie humaine (The Human Comedy). He helped to establish the traditional 
form of the novel and is generally considered to be one of the greatest 
novelists of all time.

我们在 src/main/resources 目录中有一个文本文件。

com/zetcode/ReadingLinesEx.java
package com.zetcode;

import com.google.common.base.Charsets;
import com.google.common.io.Files;
import java.io.File;
import java.io.IOException;
import java.util.List;

public class ReadingLinesEx {

    public static void main(String[] args) throws IOException {
        
        String fileName = "src/main/resources/balzac.txt";
        
        List<String> lines = Files.readLines(new File(fileName), 
                Charsets.UTF_8);
        
        for (String line: lines) {

            System.out.println(line);
        }
    }
}

该示例从 balzac.txt 文件中读取所有行,并将它们打印到控制台。

String fileName = "src/main/resources/balzac.txt";

文件名位于 src/main/resource 目录中。

List<String> lines = Files.readLines(new File(fileName), 
        Charsets.UTF_8);

使用 Files.readLines 方法,我们从 balzac.txt 文件中读取所有行。 这些行存储在字符串列表中。

for (String line: lines) {

    System.out.println(line);
}

我们遍历该列表并打印其元素。

使用 Guava 创建新文件

Files.touch 方法用于创建新文件或更新现有文件上的时间戳。 该方法类似于 Unix touch 命令。

com/zetcode/TouchFileEx.java
package com.zetcode;

import com.google.common.io.Files;
import java.io.File;
import java.io.IOException;

public class TouchFileEx {

    public static void main(String[] args) throws IOException {
        
        String newFileName = "newfile.txt";
        
        Files.touch(new File(newFileName));
    }
}

该示例在项目的根目录中创建一个 newfile.txt

使用 Guava 写入文件

Files.write 方法将数据写入文件。

com/zetcode/WriteToFileEx.java
package com.zetcode;

import com.google.common.io.Files;
import java.io.File;
import java.io.IOException;

public class WriteToFileEx {

    public static void main(String[] args) throws IOException {
        
        String fileName = "fruits.txt";
        File file = new File(fileName);
        
        String content = "banana, orange, lemon, apple, plum";
        
        Files.write(content.getBytes(), file);
    }
}

该示例将一个由水果名称组成的字符串写入 fruits.txt 文件。 该文件在项目根目录中创建。

使用 Guava 连接字符串

Joiner 使用分隔符连接文本片段(指定为数组、Iterable、varargs 或 Map)。

com/zetcode/StringJoinerEx.java
package com.zetcode;

import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import java.util.List;

public class StringJoinerEx {
    
    public static void main(String[] args) {
        
        List<String> myList = Lists.newArrayList("8", "2", "7", "10");
        
        String result = Joiner.on(",").join(myList);
        
        System.out.println(result);
    }
}

在此示例中,我们使用逗号字符连接列表的元素。

使用 Guava 分割字符串

Splitter 通过识别分隔符序列,从输入字符串中提取不重叠的子字符串。

com/zetcode/StringSplitterEx.java
package com.zetcode;

import com.google.common.base.Splitter;
import java.util.List;

public class StringSplitterEx {
    
    public static void main(String[] args) {
        
        String input = "There is a dog in the garden.";
        
        List<String> words = Splitter.on(" ").splitToList(input);
        
        for (String word: words) {
            System.out.println(word);
        }
    }
}

该示例使用 Splitter 将句子拆分为单词。

String input = "There is a dog in the garden.";

我们有一个由七个单词组成的句子。

List<String> words = Splitter.on(" ").splitToList(input);

分隔符是一个空格字符。 splitToList 方法将输入拆分为字符串列表。

第二个示例将输入拆分为三个子字符串。

com/zetcode/StringSplitterEx2.java
package com.zetcode;

import com.google.common.base.Splitter;
import java.util.List;

public class StringSplitterEx2 {
    
    public static void main(String[] args) {
        
        String input = "coin, pencil, chair, bottle, soap";
        
        List<String> words = Splitter.on(",")
                .trimResults()
                .limit(3)
                .splitToList(input);
        
        for (String word: words) {
            System.out.println(word);
        }
    }
}

此外,单词也被修剪。

Guava 前置条件

前置条件是在我们自己的方法开始时调用的简单静态方法,用于验证正确的参数和状态。 这些方法在失败时抛出 IllegalArgumentException

com/zetcode/PreconditionsEx.java
package com.zetcode;

import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.base.Splitter;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;

public class PreconditionsEx {

    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        System.out.print("Enter items: ");
        String input = br.readLine();

        List<String> items = Splitter.on(" ").splitToList(input);
        OutputItems(items);
    }

    public static void OutputItems(List<String> items) {
        checkArgument(items != null, "The list must not be null");
        checkArgument(!items.isEmpty(), "The list must not be empty");

        for (String item: items) {
            System.out.println(item);
        }
    }
}

该示例使用了两个前置条件。

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
System.out.print("Enter items: ");
String input = br.readLine();

我们从用户那里读取输入。 我们期望一个单词列表。

List<String> items = Splitter.on(" ").splitToList(input);
OutputItems(items);

指定的单词被拆分为一个列表,该列表被传递给 OutputItems 方法

checkArgument(items != null, "The list must not be null");
checkArgument(!items.isEmpty(), "The list must not be empty");

OutputItems 方法中,我们检查列表是否为 null 且为空。 使用 checkArgument 方法,我们确保表达式的有效性; 例如,列表不为 null。

使用 Guava 计算阶乘

Guava 也有用于进行数学计算的工具。 BigIntegerMath.factorial 计算阶乘。

com/zetcode/FactorialEx.java
package com.zetcode;

import com.google.common.math.BigIntegerMath;

public class FactorialEx {

    public static void main(String[] args) {

        System.out.println(BigIntegerMath.factorial(100));
    }
}

该示例打印数字 100 的阶乘。

$ gradle run -q
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

使用 Guava 计算二项式

BigIntegerMath.binomial 返回 n 和 k 的二项式系数。

com/zetcode/FactorialEx.java
package com.zetcode;

import com.google.common.math.BigIntegerMath;
import java.math.BigInteger;

public class BinomialEx {
    
    public static void main(String[] args) {
        
        BigInteger bigInt = BigIntegerMath.binomial(4, 2);
        System.out.println(bigInt);
    }
}

该示例打印 4 和 2 的二项式。

Guava CharMatcher

CharMatcher 提供了一些基本的文本处理方法。

com/zetcode/CharMatcherEx.java
package com.zetcode;

import com.google.common.base.CharMatcher;

public class CharMatcherEx {

    public static void main(String[] args) {

        String input = "Peter17";

        CharMatcher matcher = CharMatcher.JAVA_LETTER;
        String result = matcher.retainFrom(input);
        
        System.out.println(result);
    }
}

该示例从输入字符串中删除任何非字母字符。 retainFrom 方法返回一个字符串,其中包含字符序列的所有匹配字符,按顺序排列。

在第二个示例中,我们计算输入字符串中的字符数。

com/zetcode/CharMatcherEx2.java
package com.zetcode;

import com.google.common.base.CharMatcher;

public class CharMatcherEx2 {

    public static void main(String[] args) {

        String input = "Beautiful sunny day";
        
        int n1  = CharMatcher.is('n').countIn(input);
        System.out.format("Number of n characters: %d%n", n1);

        int n2  = CharMatcher.is('i').countIn(input);
        System.out.format("Number of i characters: %d", n2);
    }
}

该示例计算输入字符串中“n”和“i”字符的数量。

int n1  = CharMatcher.is('n').countIn(input);

countIn 方法返回在字符序列中找到的匹配字符数。

$ gradle run -q
Number of n characters: 2
Number of i characters: 1

CharMatcher.whitespace 确定字符是否为空格。

com/zetcode/CharMatcherEx3.java
package com.zetcode;

import com.google.common.base.CharMatcher;

public class CharMatcherEx3 {

    public static void main(String[] args) {

        String input = "   yogurt \t";

        String result = CharMatcher.whitespace().trimFrom(input);

        System.out.println(input + " and bread" );
        System.out.println(result + " and bread");
    }
}

在第三个示例中,我们从字符串中删除空格。

String result = CharMatcher.whitespace().trimFrom(input);

空格已从输入字符串中删除。

$ gradle run -q
    yogurt        and bread
 yogurt and bread

Guava Ranges

Range 允许轻松创建各种范围。 范围或间隔定义了连续值跨度的边界; 例如,从 1 到 10(含)的整数。

com/zetcode/RangeEx.java
package com.zetcode;

import com.google.common.collect.Range;

public class RangeEx {

    public static void main(String[] args) {
        
        Range<Integer> range1 = Range.closed(3, 8);
        System.out.println(range1);
        
        Range<Integer> range2 = Range.openClosed(3, 8);
        System.out.println(range2);

        Range<Integer> range3 = Range.closedOpen(3, 8);
        System.out.println(range3);
    }
}

在此示例中,我们创建了三个整数间隔。

来源

Guava Github 页面

在本文中,我们使用了 Google Guava 库。

作者

我叫 Jan Bodnar,是一名充满热情的程序员,拥有丰富的编程经验。 自 2007 年以来,我一直在撰写编程文章。 到目前为止,我已经撰写了 1,400 多篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有Java教程