提升编程效率的利器: 解析Google Guava库之集合篇Table二维映射(四)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 提升编程效率的利器: 解析Google Guava库之集合篇Table二维映射(四)

在Java开发中,我们经常使用Map数据结构来存储键值对,其中键是唯一的,可以快速查找到对应的值。但在某些场景下,我们可能需要一个更复杂的映射结构,其中键由两部分组成,类似于一个二维表格的行和列。Guava库中的Table接口正是为了满足这种需求而设计的。


提升编程效率的利器: 解析Google Guava库之集合篇Immutable(一)

提升编程效率的利器: 解析Google Guava库之集合篇Multimap(二)

提升编程效率的利器: 解析Google Guava库之集合篇BitMap(三)

一、什么是Guava Table?

Guava的Table是一种特殊的数据结构,它允许你使用两个键(通常被称为行键和列键)来映射一个值。你可以将Table视为一个二维的Map,其中每个单元格都由行键和列键唯一确定,并存储一个值。

二、Guava Table的实现类

Guava提供了几种Table的实现类,每种都有其特定的用途和性能特点:

  • HashBasedTable:这是最常用的实现,它基于哈希表来存储数据。HashBasedTable提供了快速的插入、查找和删除操作,并且不保证任何特定的键顺序。
  • TreeBasedTable:这个实现基于红黑树,它根据键的自然顺序或者提供的比较器对行键和列键进行排序。TreeBasedTable在按键顺序遍历数据时非常高效,但插入和查找操作可能比哈希表慢。
  • ImmutableTable:这是一个不可变的Table实现,它在创建时接收所有数据,并且之后不允许修改。ImmutableTable对于需要共享或发布不可变数据集的情况非常有用,同时它提供了高效的内存使用。
  • 除了上述标准实现外,你还可以通过实现Table接口来创建自定义的Table。这允许你根据特定需求定制存储和检索数据的方式。

三、如何使用Guava Table?

下面是一个Guava Table 使用示例,该示例将展示如何创建一个Table、向其添加数据、检索数据、修改数据、遍历数据,以及一些其他高级特性的使用。

import com.google.common.collect.HashBasedTable;  
import com.google.common.collect.Table;  
  
import java.util.Map;  
import java.util.Set;  
  
public class GuavaTableAdvancedExample {  
  
    public static void main(String[] args) {  
        // 创建一个Table实例  
        Table<String, String, Integer> workHoursTable = HashBasedTable.create();  
  
        // 添加数据  
        workHoursTable.put("Alice", "ProjectA", 40);  
        workHoursTable.put("Bob", "ProjectA", 30);  
        workHoursTable.put("Alice", "ProjectB", 20);  
        workHoursTable.put("Charlie", "ProjectC", 50);  
  
        // 检索数据  
        Integer aliceProjectAHours = workHoursTable.get("Alice", "ProjectA");  
        System.out.println("Alice worked " + aliceProjectAHours + " hours on ProjectA.");  
  
        // 使用row()方法获取特定行的映射  
        Map<String, Integer> aliceWorkHours = workHoursTable.row("Alice");  
        System.out.println("Alice's work hours: " + aliceWorkHours);  
  
        // 使用column()方法获取特定列的映射  
        Map<String, Integer> projectAWorkHours = workHoursTable.column("ProjectA");  
        System.out.println("Work hours on ProjectA: " + projectAWorkHours);  
  
        // 遍历表格  
        for (Table.Cell<String, String, Integer> cell : workHoursTable.cellSet()) {  
            System.out.println(cell.getRowKey() + " worked " + cell.getValue() + " hours on " + cell.getColumnKey() + ".");  
        }  
  
        // 修改数据  
        workHoursTable.put("Alice", "ProjectA", aliceProjectAHours + 5); // Alice在ProjectA上多工作了5小时  
        System.out.println("After update, Alice worked " + workHoursTable.get("Alice", "ProjectA") + " hours on ProjectA.");  
  
        // 检查是否包含某个键值对  
        boolean hasBobOnProjectB = workHoursTable.contains("Bob", "ProjectB");  
        System.out.println("Does Bob work on ProjectB? " + hasBobOnProjectB);  
  
        // 检查行键或列键是否存在  
        boolean hasRowKeyCharlie = workHoursTable.containsRow("Charlie");  
        boolean hasColumnKeyProjectD = workHoursTable.containsColumn("ProjectD");  
        System.out.println("Does the table have a row for Charlie? " + hasRowKeyCharlie);  
        System.out.println("Does the table have a column for ProjectD? " + hasColumnKeyProjectD);  
  
        // 获取所有的行键、列键或值  
        Set<String> allRowKeys = workHoursTable.rowKeySet();  
        Set<String> allColumnKeys = workHoursTable.columnKeySet();  
        Set<Integer> allValues = workHoursTable.values();  
  
        System.out.println("All row keys: " + allRowKeys);  
        System.out.println("All column keys: " + allColumnKeys);  
        System.out.println("All values: " + allValues);  
  
        // 移除数据  
        workHoursTable.remove("Alice", "ProjectB"); // Alice不再在ProjectB上工作  
        System.out.println("After removal, Alice's work hours on ProjectB: " + workHoursTable.get("Alice", "ProjectB"));  
    }  
}

在这个示例中,我们创建了一个HashBasedTable来存储员工在不同项目上的工作时间。我们展示了如何添加数据、检索特定员工在某个项目上的工作小时数、获取特定员工或特定项目的所有工作时间、遍历整个表格、修改数据、检查键的存在性、获取所有的键或值,以及移除数据。通过这个例子,你应该能够熟悉Guava Table的大部分常用方法。


果你不使用table,那就需要用嵌套Map实现,代码可能就是下面这样

需要注意的是,与Guava Table相比,嵌套的Map在处理某些操作时可能会更加繁琐,例如检查列键是否存在,因为你需要遍历所有的内部Map。此外,嵌套的Map也没有Guava Table提供的一些高级功能和优化。

 // 创建一个嵌套的Map来模拟Table  
        Map<String, Map<String, Integer>> workHoursMap = new HashMap<>();  
  
        // 添加数据  
        addWorkHours(workHoursMap, "Alice", "ProjectA", 40);  
        addWorkHours(workHoursMap, "Bob", "ProjectA", 30);  
        addWorkHours(workHoursMap, "Alice", "ProjectB", 20);  
        addWorkHours(workHoursMap, "Charlie", "ProjectC", 50);  
  
        // 检索数据  
        Integer aliceProjectAHours = getWorkHours(workHoursMap, "Alice", "ProjectA");  
        System.out.println("Alice worked " + aliceProjectAHours + " hours on ProjectA.");  
  
        // 遍历嵌套Map  
        for (Map.Entry<String, Map<String, Integer>> entry : workHoursMap.entrySet()) {  
            String employee = entry.getKey();  
            Map<String, Integer> projects = entry.getValue();  
            for (Map.Entry<String, Integer> projectEntry : projects.entrySet()) {  
                String project = projectEntry.getKey();  
                Integer hours = projectEntry.getValue();  
                System.out.println(employee + " worked " + hours + " hours on " + project + ".");  
            }  
        }  
  
        // 修改数据  
        setWorkHours(workHoursMap, "Alice", "ProjectA", aliceProjectAHours + 5); // Alice在ProjectA上多工作了5小时  
        System.out.println("After update, Alice worked " + getWorkHours(workHoursMap, "Alice", "ProjectA") + " hours on ProjectA.");  
  
        // 检查是否包含某个键值对  
        boolean hasBobOnProjectB = containsWorkHours(workHoursMap, "Bob", "ProjectB");  
        System.out.println("Does Bob work on ProjectB? " + hasBobOnProjectB);  
  
        // 检查行键是否存在  
        boolean hasRowKeyCharlie = workHoursMap.containsKey("Charlie");  
        System.out.println("Does the nested map have an entry for Charlie? " + hasRowKeyCharlie);  
  
        // 检查列键是否存在(需要遍历所有内部Map)  
        boolean hasColumnKeyProjectD = false;  
        for (Map<String, Integer> projectMap : workHoursMap.values()) {  
            if (projectMap.containsKey("ProjectD")) {  
                hasColumnKeyProjectD = true;  
                break;  
            }  
        }  
        System.out.println("Does any employee work on ProjectD? " + hasColumnKeyProjectD);  
  
        // 移除数据  
        removeWorkHours(workHoursMap, "Alice", "ProjectB"); // Alice不再在ProjectB上工作  
        System.out.println("After removal, Alice's work hours on ProjectB: " + getWorkHours(workHoursMap, "Alice", "ProjectB"));  
    }  
  
    private static void addWorkHours(Map<String, Map<String, Integer>> workHoursMap, String rowKey, String columnKey, Integer value) {  
        workHoursMap.putIfAbsent(rowKey, new HashMap<>());  
        workHoursMap.get(rowKey).put(columnKey, value);  
    }  
  
    private static Integer getWorkHours(Map<String, Map<String, Integer>> workHoursMap, String rowKey, String columnKey) {  
        Map<String, Integer> projectMap = workHoursMap.get(rowKey);  
        return projectMap != null ? projectMap.get(columnKey) : null;  
    }  
  
    private static void setWorkHours(Map<String, Map<String, Integer>> workHoursMap, String rowKey, String columnKey, Integer value) {  
        workHoursMap.putIfAbsent(rowKey, new HashMap<>());  
        workHoursMap.get(rowKey).put(columnKey, value);  
    }  
  
    private static boolean containsWorkHours(Map<String, Map<String, Integer>> workHoursMap, String rowKey, String columnKey) {  
        Map<String, Integer> projectMap = workHoursMap.get(rowKey);  
        return projectMap != null && projectMap.containsKey(columnKey);  
    }  
  
    private static void removeWorkHours(Map<String, Map<String, Integer>> workHoursMap, String rowKey, String columnKey) {  
        Map<String, Integer> projectMap = workHoursMap.get(rowKey);  
        if (projectMap != null) {  
            projectMap.remove(columnKey);  
            if (projectMap.isEmpty()) {  
                workHoursMap.remove(rowKey);  
            }  
        }  
    }  

四、Guava Table的优势

使用Guava的Table而不是嵌套的Map有几个优势:

  • 类型安全:Table明确指定了行键、列键和值的类型,减少了类型转换的错误。
  • 更易用:Table提供了直观的API来插入、检索和遍历数据,使代码更易于阅读和维护。
  • 内存效率:Table实现类针对其特定用途进行了优化,以提供高效的内存使用。
  • 不变性:通过ImmutableTable,你可以创建不可变的表格,这对于并发编程和不可变对象模式非常有用。

五、总结

Guava的Table接口提供了一种强大且灵活的方式来处理需要使用两个键映射到一个值的情况。通过使用不同的实现类,你可以根据性能需求和特定场景选择最合适的Table。在需要处理二维数据或更复杂键结构的项目中,使用Guava的Table可以大大简化代码并提高开发效率。


相关文章
|
2月前
|
缓存 Java 调度
Java并发编程:深入解析线程池与Future任务
【7月更文挑战第9天】线程池和Future任务是Java并发编程中非常重要的概念。线程池通过重用线程减少了线程创建和销毁的开销,提高了资源利用率。而Future接口则提供了检查异步任务状态和获取任务结果的能力,使得异步编程更加灵活和强大。掌握这些概念,将有助于我们编写出更高效、更可靠的并发程序。
|
3天前
|
安全 Java 开发者
Java并发编程中的锁机制解析
本文深入探讨了Java中用于管理多线程同步的关键工具——锁机制。通过分析synchronized关键字和ReentrantLock类等核心概念,揭示了它们在构建线程安全应用中的重要性。同时,文章还讨论了锁机制的高级特性,如公平性、类锁和对象锁的区别,以及锁的优化技术如锁粗化和锁消除。此外,指出了在高并发环境下锁竞争可能导致的问题,并提出了减少锁持有时间和使用无锁编程等策略来优化性能的建议。最后,强调了理解和正确使用Java锁机制对于开发高效、可靠并发应用程序的重要性。
13 3
|
1月前
|
测试技术 开发者 Python
Python 编程中的装饰器深入解析
【8月更文挑战第1天】本文将通过实例和代码演示,深入探讨 Python 中装饰器的概念、用法和高级应用。我们将从基础开始,逐步过渡到如何自定义装饰器,并展示其在日志记录、性能测试等场景下的实际用途。文章最后还将讨论装饰器的常见误区和最佳实践。
|
23天前
|
存储 算法 Java
Java中的集合框架深度解析云上守护:云计算与网络安全的协同进化
【8月更文挑战第29天】在Java的世界中,集合框架是数据结构的代言人。它不仅让数据存储变得优雅而高效,还为程序员提供了一套丰富的工具箱。本文将带你深入理解集合框架的设计哲学,探索其背后的原理,并分享一些实用的使用技巧。无论你是初学者还是资深开发者,这篇文章都将为你打开一扇通往高效编程的大门。
|
25天前
|
SQL 设计模式 安全
Java编程中的单例模式深入解析
【8月更文挑战第27天】本文旨在探索Java中实现单例模式的多种方式,并分析其优缺点。我们将通过代码示例,展示如何在不同的场景下选择最合适的单例模式实现方法,以及如何避免常见的陷阱。
|
13天前
|
存储 JSON API
Python编程:解析HTTP请求返回的JSON数据
使用Python处理HTTP请求和解析JSON数据既直接又高效。`requests`库的简洁性和强大功能使得发送请求、接收和解析响应变得异常简单。以上步骤和示例提供了一个基础的框架,可以根据你的具体需求进行调整和扩展。通过合适的异常处理,你的代码将更加健壮和可靠,为用户提供更加流畅的体验。
42 0
|
21天前
|
数据库 Windows
超详细步骤解析:从零开始,手把手教你使用 Visual Studio 打造你的第一个 Windows Forms 应用程序,菜鸟也能轻松上手的编程入门指南来了!
【8月更文挑战第31天】创建你的第一个Windows Forms (WinForms) 应用程序是一个激动人心的过程,尤其适合编程新手。本指南将带你逐步完成一个简单WinForms 应用的开发。首先,在Visual Studio 中创建一个“Windows Forms App (.NET)”项目,命名为“我的第一个WinForms 应用”。接着,在空白窗体中添加一个按钮和一个标签控件,并设置按钮文本为“点击我”。然后,为按钮添加点击事件处理程序`button1_Click`,实现点击按钮后更新标签文本为“你好,你刚刚点击了按钮!”。
61 0
|
21天前
|
开发者 编解码
界面适应奥秘:从自适应布局到图片管理,Xamarin响应式设计全解析
【8月更文挑战第31天】在 Xamarin 的世界里,构建灵活且适应性强的界面是每位开发者的必修课。本文将带您探索 Xamarin 的响应式设计技巧,包括自适应布局、设备服务协商和高效图片管理,帮助您的应用在各种设备上表现出色。通过 Grid 和 StackLayout 实现弹性空间分配,利用 Device 类检测设备类型以加载最优布局,以及使用 Image 控件自动选择合适图片资源,让您轻松应对不同屏幕尺寸的挑战。掌握这些技巧,让您的应用在多变的市场中持续领先。
25 0
|
21天前
|
设计模式 安全 Java
Java编程中的单例模式深度解析
【8月更文挑战第31天】 单例模式,作为设计模式中的经典之一,在Java编程实践中扮演着重要的角色。本文将通过简洁易懂的语言,逐步引导读者理解单例模式的本质、实现方法及其在实际应用中的重要性。从基础概念出发,到代码示例,再到高级应用,我们将一起探索这一模式如何优雅地解决资源共享和性能优化的问题。
|
21天前
|
存储 开发者 Ruby
【揭秘Ruby高手秘籍】OOP编程精髓全解析:玩转类、继承与多态,成就编程大师之路!
【8月更文挑战第31天】面向对象编程(OOP)是通过“对象”来设计软件的编程范式。Ruby作为一种纯面向对象的语言,几乎所有事物都是对象。本文通过具体代码示例介绍了Ruby中OOP的核心概念,包括类与对象、继承、封装、多态及模块混合,展示了如何利用这些技术更好地组织和扩展代码。例如,通过定义类、继承关系及私有方法,可以创建具有特定行为的对象,并实现灵活的方法重写和功能扩展。掌握这些概念有助于提升代码质量和可维护性。
25 0

推荐镜像

更多