Object als key in einer Hashmap?

justincaseof

Grünschnabel
Hi!

ich habe eine HashMap<Object, String>.
folgendes Problem tritt nun auf:
Code:
int a=9;
int b=9;
Integer ia = a;		
Integer ib = b;		// ib==ia => true

map.put(ia, "lala1");
map.put(ib, "lala2");  // überschreibt den eintrag von ia
Dass der Eintrag überschrieben wird, ist mir klar.
Es existiert deshalb die Klasse IdentityHashMap<K,V>.
ABER: Das Boxing der int-Variablen in Integer-Objekten erzeugt zwei gleiche Objekte...(gleicher hash)...damit funktionierts also auch nicht.

------------------------------------------------------------------------------------------------------------

was ich letztlich damit erreichen will soll grundlegend so aussehen/funktionieren:
(das Konstrukt sieht wild aus; ich muss es aber wirklich derartig machen...einfach gesagt, will ich per Bytecode-Engineering zu jeder primitiven Variable einer Klasse zentral eine Information halten/abrufen)

Code:
Aufruf:
method()
{
   int a=123;
   int b=123;   // gleiche Werte können vorkommen

   MyHashMap.getInstance().put(a, "variable a");
   MyHashMap.getInstance().put(b, "variable b");

   MyObject myObject = new MyObject();
   myObject.set_int_a(a);

}

Code:
class MyObject
{
   private int a;
   public void set_int_a(int arg)
   {
      this.a = arg;
   }

   method_doit()
   {
      String result = MyHashMap.getInstance().get(a);  //hier hätte ich gern "variable a" als ergebnis
   }
}

Eine Möglichkeit wäre die physische Adresse des ints als Key zu speichern....aber das ist ja nicht Sinn und Zweck von Java.

Ich danke schon jetzt für jegliche Lösungsvorschläge

mfg, der Tobi
 

Anime-Otaku

Erfahrenes Mitglied
dumme Frage, aber was macht er wenn du kein autoboxing benutzt? Sondern auf die alte Art mittels new Integer(a);?

oder ein clone?
 

justincaseof

Grünschnabel
ganz verrückte geschichte.... *lol*

Code:
private void test(int a)
	{
		Integer I = new Integer(a);
		Integer J = Integer.valueOf(a);
		Integer K = new Integer(a);
		Integer L = a;
		System.out.println( "\n" );
		System.out.println( "1-- " + I + " -> " + System.identityHashCode(I) );
		System.out.println( "2-- " + J + " -> " + System.identityHashCode(J) );
		System.out.println( "3-- " + K + " -> " + System.identityHashCode(K) );
		System.out.println( "4-- " + L + " -> " + System.identityHashCode(L) );
	}
ergibt für a=9:
1-- 9 -> 12700959
2-- 9 -> 25276323
3-- 9 -> 20051738
4-- 9 -> 25276323

und für a=199
1-- 199 -> 7494106
2-- 199 -> 23671010
3-- 199 -> 17332331
4-- 199 -> 18464898

(siehe http://www.mi.fh-wiesbaden.de/~schwan/Vorlesungen/Prog2/Skripte/Prog2US10.pdf auf seite 1 rechts unten)

ich glaub so wird es definitiv nicht funktionieren...ich schau mir grad "sun.misc.Unsafe;" an...mal schau'n ob ich da vorm feierabend noch zu nem ergebnis komm'
 

Oliver Gierke

Erfahrenes Mitglied
Also für mich ist das korrektes Verhalten. Integers sind ValueObjects, daher sollte new Integer(3) definitiv gleich einem weiteren new Integer(3) sein.

Entweder du baust dir nen wrapper der equals und hashcode entsprechend "falsch" implementiert oder eine subklasse. Ich versteh jedoch den sinn nicht ganz.

Gruß
Ollie
 

justincaseof

Grünschnabel
@Ollie
dass die Ausgabe korrektes Verhalten ist, ist mir schon klar; ich hätt's halt nur gern anders :rolleyes: ...um mein Problem ganz einfach zu lösen (ist aber nunmal nicht drinne), hätte ich gern, dieses Verhalten:
Code:
int a=1;
int b=1;
Integer ia=a;
Integer ib=b;
if (ia!=ib)
   System.out.println("das wäre zu schön um wahr zu sein");

n Wrapper bring mir nicht viel, da damit kein Boxing möglich ist...und die Integer-methoden zu überschreiben ist bekanntlich nich drin.
Ich ersetze per Bytecode-Engineering Funktionen, wie IADD, ISUB etc. durch statische externe Methoden, um von diesen Methoden aus diverse Funktionalitäten zu realisieren. Wenn ich Wrapper-Objekte an die externe Methode geben wöllte, könnte ich ja gleich alles selber mit Wrapper-Objekten realisieren und die primitiven ints weglassen - aber genau das gilt es ja zu verhindern.

Ich hätte - wie gesagt - gern eine globale Map in der ich zu jeder (primitiven bzw. auch nicht-primitiven) Variable explizit eine Information halten kann.
Mit Objekten ist das ganze kein Problem, da diese ja eindeutig über == auf "echte" Gleichheit geprüft werden können (.equal( ) nützt mir deshalb nicht viel).

Eventuell ist mein Problem mit JNI lösbar, da ich dort Pointer für die (primitiven) int-Variablen herausextrahieren könnte. Darüber muss ich aber erst noch einmal genauer nachdenken.

Viele Grüße,
der Tobi
 

Thomas Darimont

Erfahrenes Mitglied
Hallo,
Also für mich ist das korrektes Verhalten. Integers sind ValueObjects, daher sollte new Integer(3) definitiv gleich einem weiteren new Integer(3) sein.
Ist aber nicht so... bzw. ist etwas schwammig formuliert. Die sind in dem Fall equals aber nicht == . Wohingegen (Integer)1 und 1 equals und == sind.

Ich hätte - wie gesagt - gern eine globale Map in der ich zu jeder (primitiven bzw. auch nicht-primitiven) Variable explizit eine Information halten kann.
Mit Objekten ist das ganze kein Problem, da diese ja eindeutig über == auf "echte" Gleichheit geprüft werden können (.equal( ) nützt mir deshalb nicht viel).
Dann musst du dir hier eben einen Map-Wrapper schreiben der zusätzliche put/get Methoden für primitive Typen hat und diese dort intern explizit! mit einem Wrapper umhüllt (mit new Integer/Double/Float/Char/Long/Byte/Boolean/Short(primitiveValue)) um das caching beim impliziten Boxen zu vermeiden und entsprechende hashcode /equals Implementierungen bietet.


was ich letztlich damit erreichen will soll grundlegend so aussehen/funktionieren:
(das Konstrukt sieht wild aus; ich muss es aber wirklich derartig machen...einfach gesagt, will
ich per Bytecode-Engineering zu jeder primitiven Variable einer Klasse zentral eine Information halten/abrufen)

Dann würde ich mir hier einfach für jede Klasse für die du Metainformationen halten willst eine neue
(Meta)Klasse generieren, dort die benötigten Informationen ablegen und die speichern...



Java:
/**
 * 
 */
package de.tutorials;

/**
 * @author Tom
 *
 */
public class IntegerExample {

    /**
     * @param args
     */
    public static void main(String[] args) {
        Integer a = 1;
        Integer b = 1;
        
        System.out.println(a == b);
        
        a = 127;
        b = 127;
        
        System.out.println(a == b);
        
        a = 128;
        b = 128;
        
        System.out.println(a == b);

        System.out.println(new Integer(1) == new Integer(1));
    }

}

Ausgabe:
Code:
true
true
false
false

Grund: java.lang.Integer cached Integer Instanzen für Werte im Bereich von -128 bis 127.
Deshalb liefert ein == auf geboxte Integer in diesem Wertebereich true. Beim Boxing wird
implizit die Integer.valueOf(int) Methode aufgerufen, welche zuerst mal im Cache nachschaut.

Wenn statt implizitem Boxing die Werte explizit via new Integer(int) wrapped hat man auch garantiert jeweils neue Instanzen.


Gruß Tom
 

justincaseof

Grünschnabel
so erstmal THX für die große Anteilnahme an meinem Problemchen...ich weiss das wirklich zu schätzen!
@ Thomas:
Auf die Problematik mit den gecachted Werten habe ich in meinem 2. Post schon durch den Link aufmerksam machen wollen.
neue Instanzen des jeweils gleichen ints sind aber eben leider auch immer wirklich neue Objekte. Damit kann ich die ints nicht auf Gleichheit (im Sinne der Variable selber) vergleichen, sondern nur auf den Wert.

Code:
class TargetClass
{
    public void perform(int a, int b)
    {
       int result = a+b;
    }
}
daraus wird durch Bytecode-Manipulation
class TargetClass
{
    public void perform(int a, int b)
    {
       // Grundvoraussetzung hier ist, dass die Methoden-Parameter (Attribute und Rückgabewert) NICHT verändert werden dürfen.
       int result = MyMath.iAdd(a, b);
    }
}

--------------------------------------------------

run()
{
   int a=123;

   MyHashMap.getInstance().put( new Integer(a), "nicht-primitiver Integer a");

   // natürlich wird nun die instrumentierte Klasse geladen
   new TargetClass().perform(a, 123);

}

----------------------------

class MyMath
{
   public static int iAdd(int a, int b)
   {
     String information_ueber_a = MyHashMap.getInstance().get( new Integer(a) );   // nicht möglich
     int result = a + b;
   }
}
 
Zuletzt bearbeitet:

Thomas Darimont

Erfahrenes Mitglied
Hallo,

was willst du denn nun genau machen... möchtest du Zusatzinformationen zu Variablen/Bezeichnern speichern oder zu konkreten Werten primitiven Werten/ Instanzen? Das wird bei deinen Beispielen IMHO nicht so ganz klar.

Willst du beispielsweise überall im Programm zum int 4711 die Information "bubu" verfügbar haben oder soll die Informationen "bubu" nur in einem bestimmten Kontext für einen Bezeichner gelten, also
lokale Variable foo in der Methode bar der Klasse de.tutorials.Gaga?

Gruß Tom
 

justincaseof

Grünschnabel
Hi Thomas,

ich habe letzteres vor. Sorry, wenn das nicht so ganz aus meinen Beipsielen herauskommt. Vieleicht deshalb noch die kleine Erklärung zu meinem letzten Beispiel:

Ganz zu aller erst werden diverse Klassen nach dem obigen Beispiel instrumentiert/manipuliert, sodass lediglich primitive Operationen wie IADD, ISUB, DMUL, LDIV (...) durch statische externe Methoden erstezt werden. (Letztlich ersetze ich sogar die IINC -Aufrufe (Beispielsweise in for-Schleifen) durch externe Methoden.) Diese statischen Methoden nehmen die gleichen (primitiven) Datentypen entgegen, wie diese, die eigentlich behandelt werden sollten (sprich aus statt a+b wird statische_methode_addiere(a, b) ausgeführt ).
Ich möchte nicht Werte-abhängig eine Information, sondern eigentlich Variablen-abhängig eine Info angeben.

Beim Setzen ("von extern") der Variablen bar in der Klasse de.tutorials.Gaga wird gleichzeitig (von "dort extern") noch meiner HashMap gesagt: "Diese Variable bar, die du gerade in das Object lala der Klasse de.tutorials.Gaga gesetzt hast, soll die Information "bubu" besitzen.

Meine externen Methoden (auf welche alle primitiven Operationen umgeleitet worden sind) überprüfen nun beim Erhalt einer Variable, ob zu dieser (Variable; NICHT Wert) in der HashMap eine Information vorliegt und entscheiden dann, ob die Variablen wirklich einfach addiert werden können, oder ob noch zusätzlich etwas geschehen soll (z.B.: Information zu Summand a ist "inkompatibel" zu Information des Summanden b, also soll die Addition nicht ausgeführt werden, und (int) 0 zurückgegeben werden...)


Also ich will dich/euch alle nich mit so abstrusen Sachen nerven...ich bin nur schon so oft in diesem Forum auf echt geniale Lösungen gestossen, und hab' deshalb so rein prophylaktisch 'mal nen Thread aufgemacht. Mir ist klar, dass mein Vorhaben ohne exakten Kontext sinnfrei aussieht und ich mit meinem bisherigen Ansatz an dieser Stelle nicht weiterkomme...die Beispiele zeigen ja nur inhaltlich meine Vorhaben - aber ich habe Klare Vorstellungen was letztlich durch mein Vorhaben passieren soll.

...was ich eigentlich bräuchte, wären Pointer für java... :-(

THX an alle!!
 
Zuletzt bearbeitet: