Guava工具库实战:Map、List、Splitter、Joiner高效使用

一、引言:为什么选择Guava?
在Java开发中,我们经常会遇到这样的痛点:
- 创建不可变集合:JDK原生API过于繁琐
- 处理null值:需要大量的判空代码
- 字符串分割 :
String.split()功能有限,无法优雅处理空白 - 集合操作:缺少便捷的工具方法
Google Guava正是为了解决这些问题而生。它是Google开源的Java核心库,提供了大量优雅、高效的工具类,被Spring、Hibernate、MyBatis等众多知名框架广泛使用。
Guava的核心优势:
✅ 代码简洁 :一行代码完成复杂操作 ✅ 空值安全 :内置null处理机制 ✅ 性能优化 :经过Google生产环境验证 ✅ 易于使用:流畅的API设计,链式调用
本文将深入介绍Guava中最常用的四大工具:Map、List、Splitter、Joiner,通过真实案例展示它们在生产环境中的应用。
二、Guava Map:强大的映射集合

Guava提供了多种增强的Map实现,每种都针对特定场景优化。
2.1 ImmutableMap:不可变映射
特点:
- 线程安全,无需同步
- 创建后不可修改
- 性能优于Collections.unmodifiableMap()
java
import com.google.common.collect.ImmutableMap;
// 方式1:直接构建
ImmutableMap<String, Integer> map = ImmutableMap.of(
"Java", 1,
"Python", 2,
"Go", 3
);
// 方式2:Builder模式(超过5个键值对时使用)
ImmutableMap<String, String> configMap = ImmutableMap.<String, String>builder()
.put("db.host", "localhost")
.put("db.port", "3306")
.put("db.name", "test")
.put("db.user", "root")
.put("db.password", "123456")
.build();
// 方式3:从已有Map复制
Map<String, Integer> originalMap = new HashMap<>();
originalMap.put("A", 1);
originalMap.put("B", 2);
ImmutableMap<String, Integer> immutableCopy = ImmutableMap.copyOf(originalMap);
生产案例:配置管理
java
public class DatabaseConfig {
// 环境配置映射,不可变且线程安全
private static final ImmutableMap<String, String> ENV_CONFIG = ImmutableMap.of(
"dev", "jdbc:mysql://dev-db:3306/app",
"test", "jdbc:mysql://test-db:3306/app",
"prod", "jdbc:mysql://prod-db:3306/app"
);
// HTTP状态码映射
private static final ImmutableMap<Integer, String> HTTP_STATUS =
ImmutableMap.<Integer, String>builder()
.put(200, "OK")
.put(201, "Created")
.put(400, "Bad Request")
.put(401, "Unauthorized")
.put(403, "Forbidden")
.put(404, "Not Found")
.put(500, "Internal Server Error")
.build();
public static String getDbUrl(String env) {
return ENV_CONFIG.getOrDefault(env, ENV_CONFIG.get("dev"));
}
public static String getStatusMessage(int code) {
return HTTP_STATUS.getOrDefault(code, "Unknown Status");
}
}
优势:
- 避免了同步开销
- 内存占用更少(优化的内部结构)
- 编译期就能发现修改错误
2.2 BiMap:双向映射
BiMap允许你根据值反向查找键,非常适合双向映射场景。
基本用法:
java
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
// 创建BiMap
BiMap<String, Integer> userIdMap = HashBiMap.create();
userIdMap.put("Alice", 1001);
userIdMap.put("Bob", 1002);
userIdMap.put("Charlie", 1003);
// 正向查找
Integer aliceId = userIdMap.get("Alice"); // 1001
// 反向查找
BiMap<Integer, String> inverse = userIdMap.inverse();
String user = inverse.get(1002); // "Bob"
// 注意:值必须唯一,否则抛出异常
userIdMap.put("David", 1001); // 抛出IllegalArgumentException
// 强制替换
userIdMap.forcePut("David", 1001); // Alice被移除,David映射到1001
生产案例:错误码管理
java
public class ErrorCodeManager {
// 错误码与消息的双向映射
private static final BiMap<String, Integer> ERROR_CODES = HashBiMap.create();
static {
ERROR_CODES.put("USER_NOT_FOUND", 1001);
ERROR_CODES.put("INVALID_PASSWORD", 1002);
ERROR_CODES.put("ACCOUNT_LOCKED", 1003);
ERROR_CODES.put("PERMISSION_DENIED", 1004);
}
// 根据错误名获取错误码
public static Integer getErrorCode(String errorName) {
return ERROR_CODES.get(errorName);
}
// 根据错误码获取错误名
public static String getErrorName(Integer errorCode) {
return ERROR_CODES.inverse().get(errorCode);
}
// 示例:日志记录
public void logError(Integer errorCode) {
String errorName = getErrorName(errorCode);
System.out.println("Error [" + errorCode + "]: " + errorName);
}
}
2.3 Multimap:一键多值映射
Multimap允许一个键对应多个值,避免了Map<K, List<V>>的繁琐操作。
基本用法:
java
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
// 创建Multimap
Multimap<String, String> tagMap = ArrayListMultimap.create();
// 添加多个值
tagMap.put("Java", "Spring");
tagMap.put("Java", "MyBatis");
tagMap.put("Java", "Guava");
tagMap.put("Python", "Django");
tagMap.put("Python", "Flask");
// 获取某个键的所有值
Collection<String> javaTags = tagMap.get("Java");
// ["Spring", "MyBatis", "Guava"]
// 获取所有键值对
for (Map.Entry<String, String> entry : tagMap.entries()) {
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
// 移除特定值
tagMap.remove("Java", "MyBatis");
生产案例:文章标签系统
java
public class ArticleTagService {
// 文章ID -> 标签列表的映射
private Multimap<Long, String> articleTags = ArrayListMultimap.create();
// 添加标签
public void addTag(Long articleId, String tag) {
articleTags.put(articleId, tag);
}
// 批量添加标签
public void addTags(Long articleId, List<String> tags) {
articleTags.putAll(articleId, tags);
}
// 获取文章的所有标签
public Collection<String> getTags(Long articleId) {
return articleTags.get(articleId);
}
// 移除标签
public void removeTag(Long articleId, String tag) {
articleTags.remove(articleId, tag);
}
// 查找包含某标签的所有文章
public List<Long> findArticlesByTag(String tag) {
return articleTags.entries().stream()
.filter(entry -> entry.getValue().equals(tag))
.map(Map.Entry::getKey)
.collect(Collectors.toList());
}
}
// 使用示例
ArticleTagService service = new ArticleTagService();
service.addTags(1001L, Arrays.asList("Java", "Spring", "后端"));
service.addTags(1002L, Arrays.asList("Java", "MyBatis", "数据库"));
Collection<String> tags = service.getTags(1001L);
// ["Java", "Spring", "后端"]
List<Long> javaArticles = service.findArticlesByTag("Java");
// [1001, 1002]
2.4 Table:双键映射表
Table提供了类似数据库表的结构,支持行键和列键。
基本用法:
java
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
// 创建Table
Table<String, String, Integer> scoreTable = HashBasedTable.create();
// 添加数据(学生、科目、分数)
scoreTable.put("Alice", "Math", 95);
scoreTable.put("Alice", "English", 88);
scoreTable.put("Bob", "Math", 82);
scoreTable.put("Bob", "English", 90);
// 获取特定单元格的值
Integer aliceMath = scoreTable.get("Alice", "Math"); // 95
// 获取某一行
Map<String, Integer> aliceScores = scoreTable.row("Alice");
// {Math=95, English=88}
// 获取某一列
Map<String, Integer> mathScores = scoreTable.column("Math");
// {Alice=95, Bob=82}
生产案例:权限矩阵
java
public class PermissionMatrix {
// 用户角色、资源、权限的映射表
private Table<String, String, Set<String>> permissionTable = HashBasedTable.create();
public PermissionMatrix() {
initPermissions();
}
private void initPermissions() {
// 管理员权限
permissionTable.put("ADMIN", "USER", Sets.newHashSet("CREATE", "READ", "UPDATE", "DELETE"));
permissionTable.put("ADMIN", "ORDER", Sets.newHashSet("CREATE", "READ", "UPDATE", "DELETE"));
// 普通用户权限
permissionTable.put("USER", "USER", Sets.newHashSet("READ", "UPDATE"));
permissionTable.put("USER", "ORDER", Sets.newHashSet("CREATE", "READ"));
// 访客权限
permissionTable.put("GUEST", "USER", Sets.newHashSet("READ"));
permissionTable.put("GUEST", "ORDER", Sets.newHashSet("READ"));
}
// 检查权限
public boolean hasPermission(String role, String resource, String action) {
Set<String> permissions = permissionTable.get(role, resource);
return permissions != null && permissions.contains(action);
}
// 获取角色在某资源上的所有权限
public Set<String> getPermissions(String role, String resource) {
return permissionTable.get(role, resource);
}
}
三、Guava List:强大的列表工具

3.1 ImmutableList:不可变列表
基本用法:
java
import com.google.common.collect.ImmutableList;
// 方式1:直接构建
ImmutableList<String> languages = ImmutableList.of("Java", "Python", "Go");
// 方式2:Builder模式
ImmutableList<Integer> numbers = ImmutableList.<Integer>builder()
.add(1, 2, 3)
.add(4, 5)
.build();
// 方式3:从已有集合复制
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
ImmutableList<String> immutableCopy = ImmutableList.copyOf(list);
3.2 Lists工具类
创建列表:
java
import com.google.common.collect.Lists;
// 创建ArrayList并初始化
List<String> list = Lists.newArrayList("a", "b", "c");
// 创建指定容量的ArrayList
List<String> listWithCapacity = Lists.newArrayListWithCapacity(100);
// 创建LinkedList
List<Integer> linkedList = Lists.newLinkedList();
列表分割:
java
// 将大列表分割为固定大小的子列表
List<Integer> bigList = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<List<Integer>> partitions = Lists.partition(bigList, 3);
// 结果:[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
for (List<Integer> partition : partitions) {
System.out.println(partition);
}
生产案例:批量处理
java
public class BatchProcessor {
private static final int BATCH_SIZE = 100;
// 批量插入数据
public void batchInsert(List<User> users) {
// 将用户列表分割为每100个一批
List<List<User>> batches = Lists.partition(users, BATCH_SIZE);
for (List<User> batch : batches) {
// 批量插入到数据库
userDao.batchInsert(batch);
// 避免过快的插入导致数据库压力
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
// 批量发送通知
public void batchNotify(List<Long> userIds, String message) {
Lists.partition(userIds, 50).forEach(batch -> {
// 每批最多50个用户
notificationService.sendBatch(batch, message);
});
}
}
列表反转:
java
List<String> list = Lists.newArrayList("A", "B", "C", "D");
List<String> reversed = Lists.reverse(list);
// ["D", "C", "B", "A"]
列表转换:
java
List<String> names = Lists.newArrayList("alice", "bob", "charlie");
// 转换为大写
List<String> upperNames = Lists.transform(names, String::toUpperCase);
// ["ALICE", "BOB", "CHARLIE"]
// 注意:transform返回的是视图,不是新列表
笛卡尔积:
java
List<String> colors = Lists.newArrayList("Red", "Green");
List<String> sizes = Lists.newArrayList("S", "M", "L");
List<List<String>> product = Lists.cartesianProduct(colors, sizes);
// [[Red, S], [Red, M], [Red, L], [Green, S], [Green, M], [Green, L]]
生产案例:商品SKU生成
java
public class ProductSkuGenerator {
public List<ProductSku> generateSkus(Product product) {
// 获取所有属性值
List<String> colors = product.getColors(); // ["黑色", "白色"]
List<String> sizes = product.getSizes(); // ["S", "M", "L"]
List<String> materials = product.getMaterials(); // ["棉质", "涤纶"]
// 计算笛卡尔积
List<List<String>> combinations = Lists.cartesianProduct(
colors, sizes, materials
);
// 生成SKU
return combinations.stream()
.map(combo -> {
ProductSku sku = new ProductSku();
sku.setProductId(product.getId());
sku.setColor(combo.get(0));
sku.setSize(combo.get(1));
sku.setMaterial(combo.get(2));
sku.setSkuCode(generateSkuCode(combo));
return sku;
})
.collect(Collectors.toList());
}
private String generateSkuCode(List<String> attributes) {
return Joiner.on("-").join(attributes);
}
}
四、Splitter:智能字符串分割

JDK的String.split()存在诸多问题:无法优雅处理空白、空字符串等。Guava的Splitter提供了更强大的功能。
4.1 基础用法
java
import com.google.common.base.Splitter;
// 按逗号分割
String str = "a,b,c,d";
Iterable<String> parts = Splitter.on(',').split(str);
// ["a", "b", "c", "d"]
// 转换为List
List<String> list = Splitter.on(',').splitToList(str);
4.2 处理空白和空字符串
java
String messyStr = "a,b,,c, ,d, ,e";
// 去除空格并忽略空字符串
List<String> cleaned = Splitter.on(',')
.trimResults() // 去除每个元素的首尾空格
.omitEmptyStrings() // 忽略空字符串
.splitToList(messyStr);
// 结果:["a", "b", "c", "d", "e"]
对比JDK方式:
java
// JDK方式:繁琐且容易出错
String[] parts = messyStr.split(",");
List<String> result = new ArrayList<>();
for (String part : parts) {
String trimmed = part.trim();
if (!trimmed.isEmpty()) {
result.add(trimmed);
}
}
// Guava方式:一行搞定
List<String> result = Splitter.on(',')
.trimResults()
.omitEmptyStrings()
.splitToList(messyStr);
4.3 限制分割次数
java
String str = "key:value:extra:data";
// 最多分割为2部分
List<String> parts = Splitter.on(':')
.limit(2)
.splitToList(str);
// 结果:["key", "value:extra:data"]
4.4 MapSplitter:分割为Map
java
String queryString = "name=Alice&age=25&city=Beijing";
// 分割为Map
Map<String, String> params = Splitter.on('&')
.withKeyValueSeparator('=')
.split(queryString);
// 结果:{name=Alice, age=25, city=Beijing}
生产案例:URL参数解析
java
public class UrlParamParser {
private static final Splitter.MapSplitter PARAM_SPLITTER =
Splitter.on('&')
.trimResults()
.omitEmptyStrings()
.withKeyValueSeparator('=');
// 解析URL参数
public Map<String, String> parseQueryString(String url) {
int questionMarkIndex = url.indexOf('?');
if (questionMarkIndex == -1) {
return Collections.emptyMap();
}
String queryString = url.substring(questionMarkIndex + 1);
return PARAM_SPLITTER.split(queryString);
}
// 示例使用
public static void main(String[] args) {
UrlParamParser parser = new UrlParamParser();
String url = "https://api.example.com/search?keyword=guava&page=1&size=20";
Map<String, String> params = parser.parseQueryString(url);
System.out.println("关键词: " + params.get("keyword")); // guava
System.out.println("页码: " + params.get("page")); // 1
System.out.println("大小: " + params.get("size")); // 20
}
}
4.5 正则表达式分割
java
String log = "2024-01-15 10:30:45 ERROR User not found";
// 按空格分割
List<String> parts = Splitter.onPattern("\\s+")
.splitToList(log);
// 结果:["2024-01-15", "10:30:45", "ERROR", "User", "not", "found"]
生产案例:日志解析
java
public class LogParser {
private static final Splitter LOG_SPLITTER = Splitter.onPattern("\\s+")
.limit(4);
public LogEntry parseLog(String logLine) {
List<String> parts = LOG_SPLITTER.splitToList(logLine);
if (parts.size() < 4) {
throw new IllegalArgumentException("Invalid log format");
}
LogEntry entry = new LogEntry();
entry.setDate(parts.get(0));
entry.setTime(parts.get(1));
entry.setLevel(parts.get(2));
entry.setMessage(parts.get(3)); // 剩余部分作为消息
return entry;
}
}
4.6 固定长度分割
java
String str = "123456789012";
// 每3个字符分割
Iterable<String> parts = Splitter.fixedLength(3).split(str);
// ["123", "456", "789", "012"]
五、Joiner:优雅的字符串连接

5.1 基础用法
java
import com.google.common.base.Joiner;
List<String> list = Arrays.asList("Google", "Guava", "Java");
// 用逗号连接
String result = Joiner.on(", ").join(list);
// "Google, Guava, Java"
// 用换行符连接
String multiLine = Joiner.on("\n").join(list);
5.2 处理null值
java
List<String> listWithNull = Arrays.asList("A", null, "B", "C");
// 跳过null值
String skipNull = Joiner.on(", ")
.skipNulls()
.join(listWithNull);
// "A, B, C"
// 用默认值替换null
String useForNull = Joiner.on(", ")
.useForNull("N/A")
.join(listWithNull);
// "A, N/A, B, C"
5.3 连接Map
java
Map<String, String> map = new LinkedHashMap<>();
map.put("host", "localhost");
map.put("port", "8080");
map.put("path", "/api");
// 连接Map为字符串
String mapStr = Joiner.on("&")
.withKeyValueSeparator("=")
.join(map);
// 结果:"host=localhost&port=8080&path=/api"
生产案例:生成URL查询参数
java
public class UrlBuilder {
private static final Joiner.MapJoiner QUERY_JOINER =
Joiner.on('&').withKeyValueSeparator('=');
private String baseUrl;
private Map<String, String> params = new LinkedHashMap<>();
public UrlBuilder(String baseUrl) {
this.baseUrl = baseUrl;
}
public UrlBuilder addParam(String key, String value) {
if (value != null) {
params.put(key, value);
}
return this;
}
public String build() {
if (params.isEmpty()) {
return baseUrl;
}
return baseUrl + "?" + QUERY_JOINER.join(params);
}
// 示例使用
public static void main(String[] args) {
String url = new UrlBuilder("https://api.example.com/search")
.addParam("q", "guava")
.addParam("page", "1")
.addParam("size", "20")
.build();
System.out.println(url);
// https://api.example.com/search?q=guava&page=1&size=20
}
}
5.4 追加到StringBuilder
java
StringBuilder sb = new StringBuilder("Results: ");
List<String> items = Arrays.asList("A", "B", "C");
Joiner.on(", ").appendTo(sb, items);
// sb内容:"Results: A, B, C"
生产案例:SQL生成
java
public class SqlBuilder {
public String buildInsertSql(String tableName, Map<String, Object> data) {
List<String> columns = new ArrayList<>(data.keySet());
List<String> placeholders = Collections.nCopies(columns.size(), "?");
StringBuilder sql = new StringBuilder("INSERT INTO ");
sql.append(tableName).append(" (");
Joiner.on(", ").appendTo(sql, columns);
sql.append(") VALUES (");
Joiner.on(", ").appendTo(sql, placeholders);
sql.append(")");
return sql.toString();
}
// 示例
public static void main(String[] args) {
SqlBuilder builder = new SqlBuilder();
Map<String, Object> data = new LinkedHashMap<>();
data.put("name", "Alice");
data.put("age", 35);
data.put("email", "alice@example.com");
String sql = builder.buildInsertSql("users", data);
System.out.println(sql);
// INSERT INTO users (name, age, email) VALUES (?, ?, ?)
}
}
六、Guava vs JDK:性能与易用性对比

6.1 代码简洁度对比
场景:创建不可变Map
java
// JDK方式
Map<String, Integer> map = new HashMap<>();
map.put("A", 1);
map.put("B", 2);
map.put("C", 3);
Map<String, Integer> immutableMap = Collections.unmodifiableMap(map);
// Guava方式
ImmutableMap<String, Integer> immutableMap = ImmutableMap.of(
"A", 1,
"B", 2,
"C", 3
);
场景:字符串分割并处理空白
java
// JDK方式
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方式
List<String> result = Splitter.on(',')
.trimResults()
.omitEmptyStrings()
.splitToList(str);
6.2 性能对比
Guava在很多场景下性能优于JDK:
ImmutableMap vs HashMap:
- 内存占用:ImmutableMap更少(优化的数据结构)
- 访问速度:ImmutableMap略快(无锁开销)
- 线程安全:ImmutableMap无需同步
Splitter vs String.split():
- 复用性:Splitter可复用,避免重复编译正则
- 灵活性:Splitter支持链式配置
七、生产实战案例

案例一:CSV文件解析
java
public class CsvParser {
private static final Splitter CSV_SPLITTER = Splitter.on(',')
.trimResults()
.omitEmptyStrings();
public List<User> parseCsvFile(String filePath) throws IOException {
List<User> users = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
// 跳过标题行
String header = reader.readLine();
String line;
while ((line = reader.readLine()) != null) {
List<String> fields = CSV_SPLITTER.splitToList(line);
if (fields.size() >= 3) {
User user = new User();
user.setName(fields.get(0));
user.setAge(Integer.parseInt(fields.get(1)));
user.setEmail(fields.get(2));
users.add(user);
}
}
}
return users;
}
}
案例二:配置文件解析
java
public class ConfigLoader {
private static final Splitter.MapSplitter CONFIG_SPLITTER =
Splitter.on('\n')
.trimResults()
.omitEmptyStrings()
.withKeyValueSeparator('=');
public ImmutableMap<String, String> loadConfig(String configText) {
// 移除注释行
String cleanedConfig = Splitter.on('\n')
.splitToStream(configText)
.filter(line -> !line.trim().startsWith("#"))
.collect(Collectors.joining("\n"));
return ImmutableMap.copyOf(CONFIG_SPLITTER.split(cleanedConfig));
}
}
案例三:生成缓存Key
java
public class CacheKeyGenerator {
private static final Joiner KEY_JOINER = Joiner.on(':').skipNulls();
// 生成用户缓存键
public String generateUserKey(Long userId) {
return KEY_JOINER.join("user", userId);
// "user:12345"
}
// 生成订单缓存键
public String generateOrderKey(String userId, String orderId) {
return KEY_JOINER.join("order", userId, orderId);
// "order:user123:order456"
}
// 生成商品缓存键(带分类)
public String generateProductKey(String category, String productId) {
return KEY_JOINER.join("product", category, productId);
// "product:electronics:prod789"
}
}
八、最佳实践与注意事项
8.1 Splitter和Joiner复用
Splitter和Joiner是不可变的,应该声明为静态常量复用:
java
public class Constants {
// 推荐:声明为静态常量
private static final Splitter COMMA_SPLITTER = Splitter.on(',')
.trimResults()
.omitEmptyStrings();
private static final Joiner COMMA_JOINER = Joiner.on(',').skipNulls();
// 不推荐:每次都创建新实例
public void badExample(String str) {
List<String> parts = Splitter.on(',').splitToList(str); // 每次创建
}
}
8.2 ImmutableMap的大小限制
ImmutableMap.of()最多支持5个键值对,超过需要使用Builder:
java
// 错误:超过5个键值对会编译错误
ImmutableMap<String, Integer> map = ImmutableMap.of(
"A", 1, "B", 2, "C", 3, "D", 4, "E", 5, "F", 6 // 编译错误
);
// 正确:使用Builder
ImmutableMap<String, Integer> map = ImmutableMap.<String, Integer>builder()
.put("A", 1)
.put("B", 2)
.put("C", 3)
.put("D", 4)
.put("E", 5)
.put("F", 6)
.build();
8.3 BiMap的值唯一性
BiMap要求值必须唯一,重复会抛异常:
java
BiMap<String, Integer> map = HashBiMap.create();
map.put("A", 1);
map.put("B", 1); // 抛出IllegalArgumentException
// 使用forcePut强制替换
map.forcePut("B", 1); // A被移除,B映射到1
8.4 Lists.transform()的视图特性
transform()返回的是视图,不是新列表:
java
List<String> original = Lists.newArrayList("a", "b", "c");
List<String> transformed = Lists.transform(original, String::toUpperCase);
// 修改原列表会影响视图
original.set(0, "x");
System.out.println(transformed.get(0)); // "X"
// 如果需要独立的列表,应该复制
List<String> independent = new ArrayList<>(transformed);
九、总结
Guava工具库为Java开发提供了强大而优雅的解决方案:
Map工具:
- ✅ ImmutableMap:线程安全的不可变映射
- ✅ BiMap:双向查找的映射
- ✅ Multimap:一键多值的便捷操作
- ✅ Table:双键映射的强大功能
List工具:
- ✅ ImmutableList:不可变列表
- ✅ Lists.partition():批量处理的利器
- ✅ Lists.transform():函数式转换
Splitter:
- ✅ 智能处理空白和null
- ✅ 链式API,代码更简洁
- ✅ MapSplitter解析键值对
Joiner:
- ✅ 优雅的字符串连接
- ✅ 灵活的null处理
- ✅ Map和集合的统一连接