2013-06-16 1 views
3

내가 선택한 데이터 구조 설계는 실행하기가 매우 어렵다는 것을 증명하고 있으므로,이를 실행하는 방법에 대한 전문가의 의견을 구하는 대신에 다음을 제안 할 수 있기를 바랍니다. 내가하려고하는 것에 대한보다 자연스러운 데이터 구조는 다음과 같습니다. 나는 데이터 열을 읽고있다. 각 열은 하나의 변수 (Animal, Color, Crop, ... - 45 개가 있습니다)입니다. 각 데이터 행에는 해당 열의 변수 값이 있습니다. 미리 값 또는 행 수를 알지 못합니다.해시 맵의 다중 맵을위한 더 나은 데이터 구조

Animal Color Crop ... 
------------------------------------- 
cat  red  oat 
cat  blue  hay 
dog  blue  oat 
bat  blue  corn 
cat  red  corn 
dog  gray  corn 
...  ...  ... 

내가 읽고 끝났어요 때, 변수가 걸린, 각 값을 각각의 변수를 캡처하고 몇 번이나 그 변수가 같은, 그 값을했다한다 :

Animal [cat, 3][dog,2][bat, 1]... 
Color [blue, 3][red,2][gray,1]... 
Crop [corn,3][oat, 2][hay,1]... 
... 

나는 시도했다 내가 잘 할 정확히 무엇을 만든 것 같다

Map<String, Integer> eqCnts = new HashMap<String, Integer>(); 
Multimap<String, Map> ed3Dcnt = HashMultimap.create(); 
for (int i = 0; i + 1 < header.length; i++) { 
    System.out.format("Got a variable of %s\n", tmpStrKey = header[i]); 
    ed3Dcnt.put(tmpStrKey, new HashMap<String, Integer>()); 
} 

,하지만 매우 어색하고 작업에 지루한입니다 : 여러 가지 접근 방식, 내가 가까이서 보는이 같은 해시 맵의 구아바 멀티지도입니다 와 함께 nd 또한 신비한 방식으로 동작합니다 ("ed3Dcnt.put()"이 HashMap을 삽입 했음에도 불구하고 해당 ".get()"은 HashMap을 반환하지 않고 완전히 새로운 일련의 문제가 있습니다.) 결과를 가장 높은 값에서 가장 낮은 값으로 정렬하고 싶습니다만, 충분히 쉽게 할 수 있다고 생각합니다.

그렇다면 데이터 구조 디자인을 더 잘 선택하는 것이 좋습니다. 분명히 더 나은 디자인 선택이 없다면 .get()이 반환하는 콜렉션을 어떻게 사용할 수 있습니까? 원하는 모든 것은 그 슬롯에 넣은 단일 HashMap입니까?

덕분에 매우 - 에드

답변

1

는 가장 적합한가 나에게 보인다 :

HashMap<String, HashMap<String, Integer>> map= new HashMap<String, HashMap<String, Integer>>(); 

을 이제 헤더 내부 맵 추가 :

for (int i = 0; i + 1 < header.length; i++) { 
    System.out.format("Got a variable of %s\n", tmpStrKey = header[i]); 
    map.put(tmpStrKey, new HashMap<String, Integer>()); 
} 

그리고를 증가하기를 내부지도의 값 :

//we are in some for loop 
for (...) { 
    String columnKey = "animal"; //lets say we are here in the for loop 
    for (...) { 
     String columnValue = "cat"; //assume we are here 
     HashMap<String, Integer> innerMap = map.get(columnKey); 

     //increment occurence 
     Integer count = innerMap.get(columnValue); 
     if (count == null) { 
      count = 0; 
     } 
     innerMap.put(columnValue, ++count); 
    } 
} 
+0

대단히 감사합니다. 좋은 답변이며 명확하게 정리되었습니다. 그것은 저를위한 간계를했다.MultiSet (HashMap 인터페이스의 신인 오류에 대해 유감스럽게 생각한다)과 카디널리티 맵 개념에 대한 두 가지 매우 중요한 기여에 대해 @slanec과 herman에게도 많은 감사를 전한다. – EdB

+0

HashMap은 인터페이스가 아니며 Map의 하위 클래스 중 하나입니다. 지도 유형과 값 유형 모두에 대해 Map 인터페이스를 사용하는 것이 좋습니다. 'Map map = 새 HashMap >()'. 지도의 구체적인 하위 유형을 참조 할 필요는 없습니다. – herman

3

Map<String, Integer>Multiset으로 대체하여 이상한 부분을 제거 할 수 있습니다.

A multiset (or a bag)은 중복 요소를 허용하고 카운트하는 세트입니다. 사과, 배, 사과를 다시 던지십시오. 사과 두 개와 배가있는 것을 기억합니다. 기본적으로, 방금 사용했던 Map<String, Integer>에서 상상 한 것입니다.

Multiset<String> eqCounts = HashMultiset.create(); 

해당 "갔지은()"는 HashMap을 반환하지 않지만 오히려 컬렉션

당신이 일반적인 'Multimap과'인터페이스를 사용하기 때문입니다

.문서는 다음과 같이 말합니다.

그러나 Multimap 인터페이스를 직접 사용하는 경우는 거의 없습니다. 은 ListMultimap 또는 SetMultimap으로, 키를 목록 또는 집합으로 각각 매핑합니다.


그래서, 원래의 디자인에 충실하기 :

  • 각 열은 저장하고 값을 계산하는 Multiset<String> 될 것입니다.
  • 당신이 가진거야 Map<String, Multiset<String>>이 같은 열을 넣어 볼 수있는 곳 (키, 값이 열은 헤더입니다) :

    Map<String, Multiset<String>> columns = Maps.newHashMap(); 
    for (int i = 0; i < headers.length; i++) { 
        System.out.format("Got a variable of %s\n", headers[i]); 
        columns.put(headers[i], HashMultiset.<String>create()); 
    } 
    

이 라인을 읽고 값을 넣어 어디 소속 :

String[] values = line.split(" "); 
for (int i = 0; i < headers.length; i++) { 
    columns.get(headers[i]).add(values[i]); 
} 

말했다 모든, 당신은 외부 HashMap는 누가 종류의 중복 및 것을 알 수 있습니다 르 일은 여전히 ​​개선 될 수 있습니다 (충분히 좋지만 생각합니다). 그것을 더 향상 시키려면 다음의 시도 할 수 있습니다 :

  1. 대신 HashMapMultiset의 배열을 사용합니다. Afterall, 미리 열 수를 알고 있습니다.
  2. 당신은 일반적인 배열을 만드는 불편한 경우
  3. List.
  4. 그리고 아마도 가장 사용이 같은 클래스 Column를 만듭니다

    private static class Column { 
        private final String header; 
        private final Multiset<String> values; 
    
        private Column(String header) { 
         this.header = header; 
         this.values = HashMultiset.create(); 
        } 
    } 
    

    대신 헤더에 대한 String[]를 사용하고 Map<String, Multiset<String>>를 들어 값은 Column[]을 사용하십시오. headers 배열을 만드는 대신이 배열을 만들 수 있습니다.

1

1) 다중 맵 내부의 맵은 일반적으로 카디널리티 맵이라고합니다. 값 컬렉션에서 카디널리티 맵을 만들려면 일반적으로 Apache Commons Collections의 CollectionUtils.getCardinalityMap을 사용합니다.하지만 그렇게하면 안전하지 않은 (안전한 것으로 알려짐) 캐스팅이 필요합니다. 구아바를 사용하여지도를 작성하려면 먼저 변수 값을 Set<String> (고유 값 집합을 얻으려면)에두고 각 값에 Iterables.frequency()을 사용하여 계산해야한다고 생각합니다. (편집 : 또는 더욱 쉽게 : 을 사용하여 카디널리티 맵을 Multiset으로 가져옵니다.) 어쨌든, 결과 카디널리티 맵은 이미 사용중인 것과 같은 Map<String, Integer입니다.

2) 왜 멀티 맵이 필요한지 알지 못합니다. 결국 각 변수를 카디널리티 맵에 매핑하려면 Map<String, Map<String, Integer>>을 사용하십시오. EDIT : 또는 다중 세트를 카디널리티 맵으로 사용하기로 결정한 경우 Map<String, Multiset<String>>을 사용하십시오.

+0

아파치 코 몬즈 컬렉션에는 또한 ['Bag'] (http://commons.apache.org/proper/commons-collections/javadocs/api-release/org/apache/commons/collections/Bag.html)이 있습니다. Multiset의 구현 (구아바의 [Multiset'] (http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Multiset.html) ... 본질적으로 'Map ') –

+0

Multiset/Bag 또는 카디널리티 맵 사용 여부는 나중에 전체 컬렉션이 필요한지 여부에 달려 있다고 생각합니다. 예 : 수백만 행을 읽는다면 수백만 개의 요소가있는 Multiset/Bag을 가질 수 있습니다 (다중 참조를 유지하지 않고 대신 개수를 유지하도록 최적화되었을 수도 있지만). – herman