雨夜

博客即是笔记,常记常看

导航

Google Guava 实战:让 Java 代码优雅 10 倍的神器

Posted on 2025-12-25 14:04  二月无雨  阅读(0)  评论(0)    收藏  举报

原文链接https://mp.weixin.qq.com/s/P3AaQmKObPv0DlYffuz4xQ


引言:你是否也遇到这些痛点?

还在写繁琐的 null 判断?还在用笨重的 HashMap?Google 的 Guava 工具库,被 Spring、MyBatis、Hibernate 等顶级框架广泛使用,能让你的 Java 代码瞬间提升一个档次!

场景一:创建配置映射

// 传统写法(7 行)
Map<String, String> config = new HashMap<>();
config.put("dev", "jdbc:mysql://dev-db:3306/app");
config.put("test", "jdbc:mysql://test-db:3306/app");
config.put("prod", "jdbc:mysql://prod-db:3306/app");
Map<String, String> finalConfig = Collections.unmodifiableMap(config);

// Guava 写法(3 行)
ImmutableMap<String, String> config = ImmutableMap.of(
    "dev", "jdbc:mysql://dev-db:3306/app",
    "test", "jdbc:mysql://test-db:3306/app",
    "prod", "jdbc:mysql://prod-db:3306/app"
);

场景二:字符串分割处理

// 传统写法(7 行)
String str = "a,b,,c, ,d";
String[] parts = str.split(",");
List<String> result = new ArrayList<>();
for (String part : parts) {
    String trimmed = part.trim();
    if (!trimmed.isEmpty()) result.add(trimmed);
}

// Guava 写法(1 行)
List<String> result = Splitter.on(',')
    .trimResults()
    .omitEmptyStrings()
    .splitToList(str);

如果你感同身受,那么 Google Guava 就是你的救星!


Guava 是什么?凭什么这么牛?

Guava 是 Google 开源的 Java 核心工具库,其优势包括:

代码减少 70% – 一行完成复杂操作
告别 null 噩梦 – 内置优雅的空值处理
性能经过实战验证 – Google 生产环境背书
API 设计精妙 – 链式调用,流畅自然


第一部分:Map 工具 —— 映射集合的进阶玩法

1️⃣ ImmutableMap:不可变映射的力量

为什么用不可变集合?

  • 天然线程安全,无需加锁
  • 防止意外修改,避免 bug
  • 内存占用更少,性能更好

实战案例:HTTP 状态码管理

public class HttpStatusManager {
    private static final ImmutableMap<Integer, String> STATUS_MAP =
        ImmutableMap.<Integer, String>builder()
            .put(200, "OK")
            .put(201, "Created")
            .put(400, "Bad Request")
            .put(401, "Unauthorized")
            .put(404, "Not Found")
            .put(500, "Internal Server Error")
            .build();

    public static String getMessage(int code) {
        return STATUS_MAP.getOrDefault(code, "Unknown Status");
    }
}

三种创建方式

// 方式1:适合 ≤5 个键值对
ImmutableMap<String, Integer> map = ImmutableMap.of("Java", 1, "Python", 2, "Go", 3);

// 方式2:Builder 模式(推荐)
ImmutableMap<String, String> configMap = ImmutableMap.<String, String>builder()
    .put("db.host", "localhost")
    .put("db.port", "3306")
    .build();

// 方式3:从现有 Map 复制
ImmutableMap<String, Integer> copyMap = ImmutableMap.copyOf(existingMap);

2️⃣ BiMap:双向查找的神器

传统 Map 只能 key → value,反查需遍历。BiMap 支持 value → key。

BiMap<String, Integer> userMap = HashBiMap.create();
userMap.put("Alice", 10001);
userMap.put("Bob", 10002);

// 正向查找
Integer id = userMap.get("Alice"); // 10001

// 反向查找
String name = userMap.inverse().get(10002); // "Bob"

生产案例:错误码双向映射

public class ErrorCodeManager {
    private static final BiMap<String, Integer> ERROR_CODES = HashBiMap.create();
    static {
        ERROR_CODES.put("USER_NOT_FOUND", 2000);
        ERROR_CODES.put("INVALID_PASSWORD", 2001);
        ERROR_CODES.put("PERMISSION_DENIED", 2002);
    }

    public static Integer getCode(String errorName) {
        return ERROR_CODES.get(errorName);
    }

    public static String getName(Integer errorCode) {
        return ERROR_CODES.inverse().get(errorCode);
    }
}

⚠️ 注意:BiMap 的 value 必须唯一!否则抛出异常。可用 forcePut() 强制替换。


3️⃣ Multimap:告别 Map<K, List<V>> 的繁琐

传统方式需要手动判空:

if (!tagMap.containsKey("Java")) {
    tagMap.put("Java", new ArrayList<>());
}
tagMap.get("Java").add("Spring");

Guava 的 Multimap 更优雅:

Multimap<String, String> tagMap = ArrayListMultimap.create();
tagMap.put("Java", "Spring");
tagMap.put("Java", "MyBatis");

真实场景:文章标签系统

public class ArticleTagService {
    private Multimap<Long, String> articleTags = ArrayListMultimap.create();

    public void addTags(Long articleId, String... tags) {
        for (String tag : tags) {
            articleTags.put(articleId, tag);
        }
    }

    public Collection<String> getTags(Long articleId) {
        return articleTags.get(articleId);
    }

    public List<Long> findByTag(String tag) {
        return articleTags.entries().stream()
            .filter(e -> e.getValue().equals(tag))
            .map(Map.Entry::getKey)
            .collect(Collectors.toList());
    }
}

⚠️ 注意:Multimap 不是线程安全的!多线程环境下应使用:

Multimap<String, String> syncMap = Multimaps.synchronizedMultimap(ArrayListMultimap.create());

4️⃣ Table:二维表的完美实现

适用于“两个键定位一个值”的场景,如学生成绩、权限矩阵。

Table<String, String, Integer> scores = HashBasedTable.create();
scores.put("Alice", "Math", 98);
scores.put("Bob", "Math", 81);

Integer aliceMath = scores.get("Alice", "Math"); // 98
Map<String, Integer> aliceScores = scores.row("Alice");   // 整行
Map<String, Integer> mathScores = scores.column("Math");  // 整列

生产案例:权限控制矩阵

public class PermissionMatrix {
    private Table<String, String, Set<String>> permissions = HashBasedTable.create();

    public void init() {
        permissions.put("ADMIN", "USER", Sets.newHashSet("CREATE", "READ", "UPDATE", "DELETE"));
        permissions.put("USER", "ORDER", Sets.newHashSet("CREATE", "READ"));
    }

    public boolean hasPermission(String role, String resource, String action) {
        Set<String> perms = permissions.get(role, resource);
        return perms != null && perms.contains(action);
    }
}

第二部分:List 工具 —— 列表操作的艺术

1️⃣ Lists.partition:批量处理利器

public void batchInsert(List<User> users) {
    List<List<User>> batches = Lists.partition(users, 100);
    for (List<User> batch : batches) {
        userDao.batchInsert(batch);
        sleep(100); // 控制频率
    }
}

示例:

List<Integer> numbers = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);
List<List<Integer>> parts = Lists.partition(numbers, 3);
// [[1,2,3], [4,5,6], [7,8,9], [10]]

2️⃣ Lists.cartesianProduct:笛卡尔积生成器

电商 SKU 生成(颜色 × 尺寸 × 材质):

List<String> colors = Arrays.asList("黑色", "白色");
List<String> sizes = Arrays.asList("S", "M", "L");
List<String> materials = Arrays.asList("棉质", "涤纶");

List<List<String>> combinations = Lists.cartesianProduct(colors, sizes, materials);
// 自动生成 2×3×2 = 12 个组合

3️⃣ 其他实用方法

// 创建列表
List<String> list = Lists.newArrayList("a", "b", "c");

// 反转
List<String> reversed = Lists.reverse(list); // ["c", "b", "a"]

// 转换(返回视图!)
List<String> upper = Lists.transform(list, String::toUpperCase); // ["A", "B", "C"]

// 不可变列表
ImmutableList<String> immutable = ImmutableList.of("a", "b", "c");

⚠️ 注意:Lists.transform() 返回的是视图,原列表修改会影响结果。如需独立副本,应复制:

List<String> independent = new ArrayList<>(transformed);

第三部分:Splitter —— 字符串分割的正确姿势

为什么 String.split() 不够用?

  • "a,,b".split(",")["a", "", "b"](含空串)
  • "a, b , c".split(",")["a", " b ", " c "](含空格)

Guava 完美解决

List<String> result = Splitter.on(',')
    .trimResults()
    .omitEmptyStrings()
    .splitToList("a, ,b,,c");
// 结果:["a", "b", "c"]

实战案例一:URL 参数解析

private static final Splitter.MapSplitter PARSER =
    Splitter.on('&').trimResults().withKeyValueSeparator('=');

public Map<String, String> parse(String url) {
    int index = url.indexOf('?');
    if (index == -1) return Collections.emptyMap();
    String query = url.substring(index + 1);
    return PARSER.split(query);
}

// 输入:https://api.com/search?keyword=guava&page=1&size=20
// 输出:{keyword=guava, page=1, size=20}

实战案例二:日志解析

private static final Splitter SPLITTER = Splitter.onPattern("\\s+").limit(4);

public LogEntry parse(String log) {
    // "2025-11-15 12:33:17 ERROR User not found"
    List<String> parts = SPLITTER.splitToList(log);
    return new LogEntry(parts.get(0), parts.get(1), parts.get(2), parts.get(3));
}

更多功能

  • 限制分割次数
    Splitter.on(':').limit(2).splitToList("key:value:extra:data");
    // ["key", "value:extra:data"]
    
  • 固定长度分割
    Splitter.fixedLength(3).splitToList("123456789012");
    // ["123", "456", "789", "012"]
    
  • 正则分割
    Splitter.onPattern("\\s+").splitToList("2024-01-15 10:30:45 ERROR");
    

第四部分:Joiner —— 字符串连接的艺术

基础用法

// 传统 StringBuilder(繁琐)
// Guava 一行搞定:
String result = Joiner.on(", ").join(list);

处理 null 值

List<String> list = Arrays.asList("A", null, "B", "C");

// 跳过 null
Joiner.on(", ").skipNulls().join(list); // "A, B, C"

// 替换 null
Joiner.on(", ").useForNull("N/A").join(list); // "A, N/A, B, C"

实战案例一:生成 Redis 缓存 Key

private static final Joiner JOINER = Joiner.on(':').skipNulls();

public String userKey(Long userId) {
    return JOINER.join("user", userId); // "user:12345"
}

实战案例二:动态 SQL 生成

private static final Joiner COMMA_JOINER = Joiner.on(", ");

public String buildInsert(String table, Map<String, Object> data) {
    List<String> columns = new ArrayList<>(data.keySet());
    List<String> placeholders = Collections.nCopies(columns.size(), "?");
    return String.format("INSERT INTO %s (%s) VALUES (%s)",
        table, COMMA_JOINER.join(columns), COMMA_JOINER.join(placeholders));
}

实战案例三:构建 URL

private static final Joiner.MapJoiner QUERY_JOINER =
    Joiner.on('&').withKeyValueSeparator('=');

public String build() {
    return baseUrl + "?" + QUERY_JOINER.join(params);
}

实战综合案例

案例一:CSV 文件处理

private static final Splitter CSV_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
private static final Joiner CSV_JOINER = Joiner.on(',').useForNull("");

public List<String> parseLine(String line) {
    return CSV_SPLITTER.splitToList(line);
}

public String buildLine(List<String> fields) {
    return CSV_JOINER.join(fields);
}

案例二:配置文件管理

private static final Splitter.MapSplitter CONFIG_SPLITTER =
    Splitter.on('\n').trimResults().omitEmptyStrings().withKeyValueSeparator('=');

public ImmutableMap<String, String> loadConfig(String content) {
    String cleaned = Splitter.on('\n')
        .splitToStream(content)
        .filter(line -> !line.trim().startsWith("#"))
        .collect(Collectors.joining("\n"));
    return ImmutableMap.copyOf(CONFIG_SPLITTER.split(cleaned));
}

案例三:批量任务处理

public void processTasks(List<Task> tasks) {
    Lists.partition(tasks, BATCH_SIZE).forEach(batch -> {
        String batchId = Joiner.on("-").join("batch", System.currentTimeMillis(), batch.size());
        processBatch(batchId, batch);
    });
}

⚡ 性能对比:Guava vs JDK

场景 JDK 代码行数 Guava 代码行数 减少比例
不可变 Map 创建 7 行 3 行 57%
字符串分割处理 7 行 1 行 86%
双向映射查找 15 行 2 行 87%
列表批量分割 10 行 1 行 90%

性能优势

  • ImmutableMap vs HashMap

    • 内存占用减少 20–30%
    • 访问速度提升 5–10%
    • 天然线程安全,无锁开销
  • Splitter vs String.split()

    • 可复用实例,避免重复编译
    • 支持链式配置,功能更强

最佳实践与注意事项

✅ 推荐做法

  1. 复用 Splitter 和 Joiner 实例

    // 推荐:静态常量
    private static final Splitter COMMA_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
    
  2. ImmutableMap 超过 5 个键值对用 Builder

    // 正确
    ImmutableMap.<String, Integer>builder().put("A", 1)...build();
    // 错误:of() 最多支持 5 个参数
    
  3. 注意 Lists.transform() 返回视图

⚠️ 常见陷阱

  1. BiMap 值必须唯一 → 使用 forcePut() 强制替换
  2. Multimap 非线程安全 → 多线程下用 Multimaps.synchronizedMultimap()

快速上手指南

1. 添加 Maven 依赖

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>32.1.3-jre</version>
</dependency>

2. 立即应用的 5 个场景

场景 推荐 Guava 工具
配置管理 ImmutableMap
错误码映射 BiMap
文章标签 Multimap
批量处理 Lists.partition
字符串操作 Splitter + Joiner