# 3-dimensionale Datenstruktur



## boesi (19. Mai 2007)

moin

Ich hab eine Anzahl von Personen und Themen, die ich ein folgender simpler Struktur zu speichern gedenke...

```
public class Data {
	class Quest {
		String name;
		...
	}
	class Person {
		String name;
		...
	}
	HashMap<Number, Quest> quests = new HashMap<Number, Quest>();
	HashMap<Number, Person> persons = new HashMap<Number, Person>();
	...
```
Die einzelnen Personen und Themen werden jeweils über eine eindeutige ID referenziert, die aber weder bei 0 anfängt noch fortlaufend ist. Die Anzahl der Personen und Themen werden zur Laufzeit ermittelt und sind dann konstant.

Nun zum Problem :wink: 
Die Personen sind in Gruppen eingeteilt und jede Person hat über jede Person in ihrer Gruppe zu jedem Thema etwas (ein Byte) zu sagen. Das bedeutet zu jeder Gruppe hab ich letztendlich eine Art Datenwürfel.

Meine Frage ist nun - in was für einer Struktur speicher ich diesen Würfel?
Meine 1. Idee war eine verschachtelte HashMap:

```
HashMap<Number, HashMap<Number, HashMap<Number, HashMap<Number, Number>>>> groups = 
	new HashMap<Number, HashMap<Number, HashMap<Number, HashMap<Number, Number>>>>
```
Naja lesbarer Code sieht anders aus. :autsch: 
Meine 2. Idee war ein (3-4)-dimensionales Array. Da ich aber die eigentlichen Personen- & Themen-IDs nicht als Index verwenden kann, müsste ich zusätzliche interne IDs einführen - macht den Code auch nicht grad verständlicher.

Vielleicht hat ja jemand noch ne vernüftige Idee? Vielleicht auch im Hinblick darauf, dass am Ende, wenn jede Person ihren Senf abgegeben hat, der ganze Kram übers Netz an ein anderes Programm (eventuell auch Java, steht aber noch nicht fest) übertragen werden soll. Dieses Programm soll mit den Daten dann ne ganze Menge rechnen ...


Vielen Dank schon mal für eure Vorschläge
cu boesi


----------



## Marco13 (21. Mai 2007)

Die erste Möglichkeit: Benenn' das ganze einfach um - dann ist es schon viel übersichtlicher:

```
// Java-typedefs ;-)
class Map1D extends HashMap<Number, Number>{ /* nix */ }
class Map2D extends HashMap<Number, Map1D>{ /* nix */ }
class Map3D extends HashMap<Number, Map3D>{ /* nix */ }
class Map4D extends HashMap<Number, Map3D>{ /* nix */ }

Map4D groups = new Map4D();
```
Aber das jetzt nicht zuuu ernst nehmen :wink:

Vielleicht wäre es grundsätzlich sinnvoler, Klassen zu erstellen, die eine gewisse "Unter-Funktionalität" für die einzelnen Dimensionen bereitstellen (also DOCH sowas ähnliches wie die oben genannten 'typedefs' :wink: ). Aber ich gehe davon aus, dass du weißt, WAS du tust, und es nur um die Frage geht, WIE du (_genau DAS_) tun könntest. (Nicht dass dafür ein Anlaß bestünde - aber ich TUE es einfach :roll: )

Dann könntest du dir einfach eine Klasse basteln, die nicht EINEN Wert auf einen anderen Wert mappt, sondern VIER. Hab das hier mal gemacht. Die Klasse stellt die wichtigsten Methoden des Map-interfaces zur Verfügung (kannst du selbst ergänzen, falls nötig). Allerdings wird das Map-Interface NICHT implementiert. Wenn man die Map beliebig-dimensional halten will (und das wollte ich) dann müssen die "Varargs" immer am Ende stehen, d.h. man kann die "put"-Methode nicht schreiben als

```
public void put(KeyType ... keyElements, ValueType value) { ... }
```
sondern muss schreiben

```
public void put(ValueType value, KeyType ... keyElements) { ... }
```
Wenn man NUR eine bestimmte Map-Art braucht (z.B. nur 4-dimensional) dann könnte man das vielleicht (!) so implementieren, dass das Map-Interface erfüllt wird, indem man als KeyType einen Array verwendet (hab ich jetzt aber nicht ausprobiert).

Wie auch immer. Hier der Code.


```
import java.util.*;

class MultiDimensionalMap<KeyType, ValueType>
{

    public static void main(String args[])
    {
        MultiDimensionalMap<Number, Number> m = new MultiDimensionalMap<Number, Number>(3);

        // put(Value, KeyElement, KeyElement, ...)
        m.put(123, 1, 2, 3);
        m.put(456, 4, 5, 6);
        m.put(789, 7, 8, 9);
        m.put(111, 1, 0, 0);
        m.put(222, 1, 0); // Only valid if DIM_CHECK==false
        m.put(333, 1);    // Only valid if DIM_CHECK==false
        m.put(444, 0, 0, 1);

        System.out.println("get 123: "+m.get(1,2,3));
        System.out.println("get 456: "+m.get(4,5,6));
        System.out.println("get 789: "+m.get(7,8,9));
        System.out.println("size "+m.size());
        System.out.println("remove 456: "+m.remove(4,5,6));
        System.out.println("size "+m.size());
        System.out.println("containsKey 456: "+m.containsKey(4,5,6));
        System.out.println("containsKey 789: "+m.containsKey(7,8,9));
        System.out.println("containsValue 456: "+m.containsValue(456));
        System.out.println("containsValue 789: "+m.containsValue(789));
        System.out.println("get 100: "+m.get(1,0,0));
        System.out.println("get 1: "+m.get(1)); // Only valid if DIM_CHECK==false
        System.out.println("get 001: "+m.get(0,0,1));

    }


    private static class KeyTuple<KeyType>
    {
        private KeyType keyElements[];

        public KeyTuple(KeyType ... keyElements)
        {
            this.keyElements = keyElements;
        }

        public int hashCode()
        {
            int hashCode = 0;
            for (int i=0; i<keyElements.length; i++)
            {
                hashCode ^= keyElements[i].hashCode();
            }
            return hashCode;
        }

        public boolean equals(Object object)
        {
            if (!(object instanceof KeyTuple))
            {
                return false;
            }
            KeyTuple other = (KeyTuple) object;
            for (int i=0; i<keyElements.length; i++)
            {
                if (!keyElements[i].equals(other.keyElements[i]))
                {
                    return false;
                }
            }
            return true;
        }
    }

    private static final boolean DIM_CHECK = false;

    private Map<KeyTuple, ValueType> map = new HashMap<KeyTuple, ValueType>();

    private int dimension;

    public MultiDimensionalMap(int dimension)
    {
        this.dimension = dimension;
    }

    private void dimCheck(KeyType ... keyElements)
    {
        if (keyElements == null)
        {
            throw new IllegalArgumentException("Dimension must be "+dimension);
        }
        if (keyElements.length != dimension)
        {
            throw new IllegalArgumentException("Dimension must be "+dimension+
                                               " but is "+keyElements.length);
        }
    }


    public void put(ValueType value, KeyType ... keyElements)
    {
        if (DIM_CHECK)
        {
            dimCheck(keyElements);
        }
        map.put(new KeyTuple<KeyType>(keyElements), value);
    }

    public ValueType get(KeyType ... keyElements)
    {
        if (DIM_CHECK)
        {
            dimCheck(keyElements);
        }
        return map.get(new KeyTuple<KeyType>(keyElements));
    }

    public ValueType remove(KeyType ... keyElements)
    {
        if (DIM_CHECK)
        {
            dimCheck(keyElements);
        }
        return map.remove(new KeyTuple<KeyType>(keyElements));
    }

    public boolean containsKey(KeyType ... keyElements)
    {
        if (DIM_CHECK)
        {
            dimCheck(keyElements);
        }
        return map.containsKey(new KeyTuple<KeyType>(keyElements));
    }

    public boolean containsValue(ValueType value)
    {
        return map.containsValue(value);
    }

    public int size()
    {
        return map.size();
    }

    public void clear()
    {
        map.clear();
    }


}
```


----------



## boesi (23. Mai 2007)

Marco13 hat gesagt.:
			
		

> Aber ich gehe davon aus, dass du weißt, WAS du tust, und es nur um die Frage geht, WIE du (_genau DAS_) tun könntest. (Nicht dass dafür ein Anlaß bestünde - aber ich TUE es einfach :roll: )


Wenn ich wirklich wüßte, was ich tue, würd' ich was anderes tun - sonst wird's ja langweilig 



> Wie auch immer. Hier der Code.


Danke für die kurze Lektion "Wie mach ich aus einer mehr-dimensionalen Struktur eine lineare". Und dann auch noch gleich mit fertigem Code. Die Idee, als Schlüssel kurzerhand ein Array bzw eine Hilfsklasse zu verwenden, ist so simpel, dass es schon wieder peinlich ist, dass ich da nicht selbst drauf gekommen bin. Aber so isses ja immer :applaus:

Bin dann aber von dem Map-Interface komplett weggegangen - in meinem Fall stehen die Datentypen und die Zahl der Unterschlüssel fest. Ausserdem brauch ich Methoden wie clear oder remove nicht. Dafür muss ich noch son Quatsch wie "Haben alle Personen für ein Thema ihre Meinung gesagt" implementieren. Letztenendlich ist für den Fall eine generalisierte Lösung nur dann sinnvoll, wenn sie bereits fertig existiert. Andernfalls wäre das Programmierer-Sünde Nr. 4 (The seven sins of programmers)


cu boesi


----------

