1. 概述

本教程将介绍 Google Guava 库中的 Table 接口及其多种实现方式。

Guava 的 Table 是一种集合结构,用于表示类似表格的数据结构,包含行(row)、列(column)以及对应的单元格值(cell value)。行和列共同组成一个有序的键对(key pair),用于定位单元格中的值。

2. Guava 的 Table 简介

2.1. Maven 依赖

首先,在 pom.xml 中添加 Guava 的依赖:

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

最新版本可以在 Maven Central 查看。

2.2. 原理说明

✅ 如果要用 Java 原生的集合来模拟 Guava 的 Table,可以把它理解为一个嵌套的 Map:Map<RowKey, Map<ColumnKey, Value>>

Table 实际上是一种特殊的 Map,它允许通过两个键(行键和列键)的组合来定位一个值。举个例子,可以用来表示大学与课程、剩余席位之间的关系:Map<UniversityName, Map<CourseName, SeatCount>>

📌 实战场景中,Table 也非常适合用于像战舰游戏的棋盘这种二维数据结构。

3. 创建 Table 的几种方式

创建 Table 实例有以下几种常见方式:

  • 使用 HashBasedTable.create(),底层使用 LinkedHashMap

    Table<String, String, Integer> universityCourseSeatTable = HashBasedTable.create();
    
  • 如果需要对行键和列键进行排序,可以使用 TreeBasedTable.create(),底层是 TreeMap

    Table<String, String, Integer> universityCourseSeatTable = TreeBasedTable.create();
    
  • 如果事先知道行和列的键,并且数据结构固定,可以使用 ArrayTable.create()

    List<String> universityRowTable = Lists.newArrayList("Mumbai", "Harvard");
    List<String> courseColumnTables = Lists.newArrayList("Chemical", "IT", "Electrical");
    Table<String, String, Integer> universityCourseSeatTable = ArrayTable.create(universityRowTable, courseColumnTables);
    
  • 如果需要一个不可变的 Table,可以使用 ImmutableTable,通过 Builder 模式构建:

    Table<String, String, Integer> universityCourseSeatTable = ImmutableTable.<String, String, Integer>builder()
      .put("Mumbai", "Chemical", 120)
      .build();
    

4. 使用示例

4.1. 获取值

如果你知道行键和列键,可以直接通过 get(rowKey, columnKey) 获取值:

@Test
public void givenTable_whenGet_returnsSuccessfully() {
    Table<String, String, Integer> universityCourseSeatTable = HashBasedTable.create();
    universityCourseSeatTable.put("Mumbai", "Chemical", 120);
    universityCourseSeatTable.put("Mumbai", "IT", 60);
    universityCourseSeatTable.put("Harvard", "Electrical", 60);
    universityCourseSeatTable.put("Harvard", "IT", 120);

    int seatCount = universityCourseSeatTable.get("Mumbai", "IT");
    Integer seatCountForNoEntry = universityCourseSeatTable.get("Oxford", "IT");

    assertThat(seatCount).isEqualTo(60);
    assertThat(seatCountForNoEntry).isEqualTo(null);
}

4.2. 判断是否存在

可以通过以下方式判断 Table 中是否存在某个条目:

  • 判断某行某列是否存在:contains(rowKey, columnKey)
  • 判断某列是否存在:containsColumn(columnKey)
  • 判断某行是否存在:containsRow(rowKey)
  • 判断某个值是否存在:containsValue(value)
@Test
public void givenTable_whenContains_returnsSuccessfully() {
    Table<String, String, Integer> universityCourseSeatTable = HashBasedTable.create();
    universityCourseSeatTable.put("Mumbai", "Chemical", 120);
    universityCourseSeatTable.put("Mumbai", "IT", 60);
    universityCourseSeatTable.put("Harvard", "Electrical", 60);
    universityCourseSeatTable.put("Harvard", "IT", 120);

    boolean entryIsPresent = universityCourseSeatTable.contains("Mumbai", "IT");
    boolean courseIsPresent = universityCourseSeatTable.containsColumn("IT");
    boolean universityIsPresent = universityCourseSeatTable.containsRow("Mumbai");
    boolean seatCountIsPresent = universityCourseSeatTable.containsValue(60);

    assertThat(entryIsPresent).isTrue();
    assertThat(courseIsPresent).isTrue();
    assertThat(universityIsPresent).isTrue();
    assertThat(seatCountIsPresent).isTrue();
}

4.3. 删除条目

通过 remove(rowKey, columnKey) 删除指定条目:

@Test
public void givenTable_whenRemove_returnsSuccessfully() {
    Table<String, String, Integer> universityCourseSeatTable = HashBasedTable.create();
    universityCourseSeatTable.put("Mumbai", "Chemical", 120);
    universityCourseSeatTable.put("Mumbai", "IT", 60);

    int seatCount = universityCourseSeatTable.remove("Mumbai", "IT");

    assertThat(seatCount).isEqualTo(60);
    assertThat(universityCourseSeatTable.remove("Mumbai", "IT")).isNull();
}

4.4. 获取某一列的映射

使用 column(columnKey) 可以获取某列的行键到值的映射:

@Test
public void givenTable_whenColumn_returnsSuccessfully() {
    Table<String, String, Integer> universityCourseSeatTable = HashBasedTable.create();
    universityCourseSeatTable.put("Mumbai", "Chemical", 120);
    universityCourseSeatTable.put("Mumbai", "IT", 60);
    universityCourseSeatTable.put("Harvard", "Electrical", 60);
    universityCourseSeatTable.put("Harvard", "IT", 120);

    Map<String, Integer> universitySeatMap = universityCourseSeatTable.column("IT");

    assertThat(universitySeatMap).hasSize(2);
    assertThat(universitySeatMap.get("Mumbai")).isEqualTo(60);
    assertThat(universitySeatMap.get("Harvard")).isEqualTo(120);
}

4.5. 获取列的完整 Map 结构

使用 columnMap() 获取 Map<ColumnKey, Map<RowKey, Value>>

@Test
public void givenTable_whenColumnMap_returnsSuccessfully() {
    Table<String, String, Integer> universityCourseSeatTable = HashBasedTable.create();
    universityCourseSeatTable.put("Mumbai", "Chemical", 120);
    universityCourseSeatTable.put("Mumbai", "IT", 60);
    universityCourseSeatTable.put("Harvard", "Electrical", 60);
    universityCourseSeatTable.put("Harvard", "IT", 120);

    Map<String, Map<String, Integer>> courseKeyUniversitySeatMap = universityCourseSeatTable.columnMap();

    assertThat(courseKeyUniversitySeatMap).hasSize(3);
    assertThat(courseKeyUniversitySeatMap.get("IT")).hasSize(2);
    assertThat(courseKeyUniversitySeatMap.get("Electrical")).hasSize(1);
    assertThat(courseKeyUniversitySeatMap.get("Chemical")).hasSize(1);
}

4.6. 获取某一行的映射

使用 row(rowKey) 获取某行的列键到值的映射:

@Test
public void givenTable_whenRow_returnsSuccessfully() {
    Table<String, String, Integer> universityCourseSeatTable = HashBasedTable.create();
    universityCourseSeatTable.put("Mumbai", "Chemical", 120);
    universityCourseSeatTable.put("Mumbai", "IT", 60);
    universityCourseSeatTable.put("Harvard", "Electrical", 60);
    universityCourseSeatTable.put("Harvard", "IT", 120);

    Map<String, Integer> courseSeatMap = universityCourseSeatTable.row("Mumbai");

    assertThat(courseSeatMap).hasSize(2);
    assertThat(courseSeatMap.get("IT")).isEqualTo(60);
    assertThat(courseSeatMap.get("Chemical")).isEqualTo(120);
}

4.7. 获取所有行键

使用 rowKeySet() 获取所有的行键:

@Test
public void givenTable_whenRowKeySet_returnsSuccessfully() {
    Table<String, String, Integer> universityCourseSeatTable = HashBasedTable.create();
    universityCourseSeatTable.put("Mumbai", "Chemical", 120);
    universityCourseSeatTable.put("Mumbai", "IT", 60);
    universityCourseSeatTable.put("Harvard", "Electrical", 60);
    universityCourseSeatTable.put("Harvard", "IT", 120);

    Set<String> universitySet = universityCourseSeatTable.rowKeySet();

    assertThat(universitySet).hasSize(2);
}

4.8. 获取所有列键

使用 columnKeySet() 获取所有的列键:

@Test
public void givenTable_whenColKeySet_returnsSuccessfully() {
    Table<String, String, Integer> universityCourseSeatTable = HashBasedTable.create();
    universityCourseSeatTable.put("Mumbai", "Chemical", 120);
    universityCourseSeatTable.put("Mumbai", "IT", 60);
    universityCourseSeatTable.put("Harvard", "Electrical", 60);
    universityCourseSeatTable.put("Harvard", "IT", 120);

    Set<String> courseSet = universityCourseSeatTable.columnKeySet();

    assertThat(courseSet).hasSize(3);
}

5. 总结

本文介绍了 Guava 库中 Table 接口的常用方法和实现方式。它提供了一种非常直观的方式来处理二维数据结构,特别适用于需要通过行键和列键共同定位值的场景。

📌 示例代码可以在 GitHub 项目 中找到,项目基于 Maven 构建,导入即可运行。


原始标题:Guide to Guava Table

« 上一篇: Spark Java 框架入门