Guava Table을 활용한 다차원 데이터 구성 기법

다차원 데이터 구조 설계

Guava의 Table은 기본적으로 행과 열로 구성된 2차원 자료구조입니다. 하지만 실무에서는 날짜, 지역, 카테고리, 지표 등 여러 축을 동시에 다루는 다차원 데이터를 자주 마주하게 됩니다. 이 경우 Table을 중첩하거나 다른 컬렉션과 조합하여 고차원 구조를 모방할 수 있습니다.

중첩 Table로 3차원 구현

가장 직관적인 방법은 Table의 값 타입을 또 다른 Table으로 선언하는 것입니다. 예를 들어 시점-지점-항목이라는 세 축을 표현하려면 다음과 같이 구성합니다.

import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import org.junit.jupiter.api.Test;

public class TripleAxisTableTest {

    @Test
    void demonstrateNestedStructure() {
        // 시점(행) × 지점(열) → 항목-수치 Table
        Table<String, String, Table<String, Double>> timeLocationMetrics = HashBasedTable.create();

        // 첫 번째 시점-지점 조합의 데이터 생성
        Table<String, Double> seoulMetrics = HashBasedTable.create();
        seoulMetrics.put("매출액", 1250.5);
        seoulMetrics.put("영업이익", 180.3);

        // 두 번째 시점-지점 조합의 데이터 생성
        Table<String, Double> busanMetrics = HashBasedTable.create();
        busanMetrics.put("매출액", 890.2);
        busanMetrics.put("영업이익", 95.7);

        // 상위 Table에 등록
        timeLocationMetrics.put("2024-Q3", "서울", seoulMetrics);
        timeLocationMetrics.put("2024-Q3", "부산", busanMetrics);

        // 계층적 조회
        Table<String, Double> retrieved = timeLocationMetrics.get("2024-Q3", "서울");
        Double revenue = retrieved.get("매출액");
        
        System.out.printf("2024년 3분기 서울 매출: %.1f%n", revenue);
    }
}

이 패턴의 핵심은 타입 파라미터를 통해 각 계층의 역할을 명확히 구분하는 것입니다. Table<R, C, Table<R2, C2, V>> 형태로 선언하면 직관적인 API 체인을 유지할 수 있습니다.

다중 루프를 통한 전체 순회

중첩된 구조에서 모든 데이터를 탐색하려면 각 계층별로 키 집합을 얻어 반복해야 합니다.

@Test
void traverseAllDimensions() {
    Table<String, String, Table<String, Double>> dataCube = buildSampleData();
    
    for (String period : dataCube.rowKeySet()) {
        for (String branch : dataCube.columnKeySet()) {
            Table<String, Double> metrics = dataCube.get(period, branch);
            if (metrics == null) continue;
            
            System.out.printf("[%s / %s]%n", period, branch);
            metrics.cellSet().forEach(cell -> 
                System.out.printf("  %s: %.2f%n", cell.getRowKey(), cell.getValue())
            );
        }
    }
}

cellSet()을 활용하면 내부 Table의 행-값 쌍을 효율적으로 순회할 수 있습니다. null 크는 반드시 필요하며, 특히 희소(sparse)한 데이터에서는 빈 셀이 많이 발생할 수 있습니다.

Map과 조합한 4차원 이상 확장

추가 축이 필요하다면 Map을 외부에 배치하여 차원을 확장합니다. 아래는 연도-월-지역-지표라는 4차원 구조를 구현한 예시입니다.

import java.util.HashMap;
import java.util.Map;

public class HyperCubeTableTest {

    @Test
    void fourDimensionalLookup() {
        // 연도 → (월 × 지역 → 지표-값 Table)
        Map<Integer, Table<String, String, Table<String, Integer>>> annualData = new HashMap<>();

        // 최하위: 지표 측정값
        Table<String, Integer> measurements = HashBasedTable.create();
        measurements.put("방문자수", 15420);
        measurements.put("전환율", 340);

        // 중간: 월-지역 매핑
        Table<String, String, Table<String, Integer>> monthlyRegional = HashBasedTable.create();
        monthlyRegional.put("3월", "강남", measurements);

        // 최상위: 연도 인덱싱
        annualData.put(2024, monthlyRegional);

        // 역순으로 조회
        Integer visitors = annualData.get(2024)
                                     .get("3월", "강남")
                                     .get("방문자수");
        
        System.out.println("2024년 3월 강남 방문자수: " + visitors);
    }
}

이 방식의 장점은 각 계층마다 적합한 자료구조를 선택할 수 있다는 점입니다. 예를 들어 연도 축은 Map으로, 월-지역은 Table로, 지표-값은 또 다른 Table로 구성하여 접근 패턴에 맞게 최적화할 수 있습니다.

설계 시 고려사항

  • 타입 안전성: 깊게 중첩될수록 제네릭 선언이 복잡해지므로, 별도의 래퍼 클래스나 타입 별칭을 고려하세요.
  • 메모리 오버헤드: Guava Table은 편의성을 위해 추가 객체를 생성하므로, 대용량 데이터에서는 직접 배열 기반 구조를 검토해야 합니다.
  • 일관성 없는 키: 각 계층의 키 타입이 달라도 무방하며, 이는 도메인 개념을 자연스럽게 반영할 수 있게 합니다.

태그: Guava Table Java Collections Multidimensional Data Data Structure

6월 14일 19:02에 게시됨