# Zwei unbestimmte Objekte vergleichen...



## Guest (19. Jul 2004)

Hi,

kennt jemand von Euch einen kürzere Alternative 
um Objekte unbestimmten Typs zu vergleichen?
Es geht mir speziell um die "primitiven" Typen,
die speziel gecastet werden müssen.
Wichtig ist mir dabei, dass der Vergleich kein "Garbage" produziert.
Die übergebenen Objekte sind jeweils vom gleichen Typ.

```
private static boolean compare(Object a, Object b)
  {
    if(a == null)
      return b==null;
    if(b == null)
      return false;

    // Kein Array, dann normal equals
    if(!a.getClass().isArray())
      return a.equals(b);
    else
    {
      // Sonst, wenn primitiver Typ
      if(a.getClass().getComponentType().isPrimitive())
      {
        // a ist "primitive", b nicht, raus mit false
        if(!b.getClass().getComponentType().isPrimitive())
          return false;
        if(a.getClass().getComponentType() == char.class)
          return Arrays.equals((char[])a, (char[])b);
        else if(a.getClass().getComponentType() == int.class)
          return Arrays.equals((int[])a, (int[])b);
        else if(a.getClass().getComponentType() == long.class)
          return Arrays.equals((long[])a, (long[])b);
        else if(a.getClass().getComponentType() == double.class)
          return Arrays.equals((double[])a, (double[])b);
        else if(a.getClass().getComponentType() == short.class)
          return Arrays.equals((short[])a, (short[])b);
        else if(a.getClass().getComponentType() == float.class)
          return Arrays.equals((float[])a, (float[])b);
        return false;
      }
      else
      {
        // b ist "primitive", a war's aber nicht, raus mit false
        if(b.getClass().getComponentType().isPrimitive())
          return false;
        // Object-Array, von welchem Typ auch immer
        return Arrays.equals((Object[])a, (Object[])b);  
      }
    }
  }
```
Gruß,
Michael


----------



## bygones (19. Jul 2004)

mhm - ich versteh zwar net den sinn und zweck der methode, aber wenn du schon die arrays miteinander vergleichst mach doch einfach:

```
public static boolean equals(Object[] a, Object[] a2)
```
http://java.sun.com/j2se/1.4.2/docs/api/java/util/Arrays.html#equals(java.lang.Object[],%20java.lang.Object[])



> In other words, the two arrays are equal if they contain the same elements in the same order. Also, two array references are considered equal if both are null.


----------



## Guest (19. Jul 2004)

Hi,

danke für die schnelle Antwort.
Das Problem ist eben, dass die übergebenen Objekte alles Mögliche sein können 
aber jeweils zwei vom gleichen Typ (oder auch null).
z.B.

char[] a= new char[] {'a', 'b', 'c'};
char[] b= new char[] {'a', 'b'};
char[] c= null;
compare(a, b);
compare(a, c);

----
Integer a = new Integer(1);
Integer b = new Integer(2);
compare(a, b);

----
ComplexModel a = ...
ComplexModel b = ...
compare(a, b);

Sinn und Zweck des ganzen ist die equals Methoden komplexer Objekte
zu vereinfachen.
In einer equals Methode taucht dann für jedes Attribut folgendes auf:

```
public boolean equals(Object o)
  {
    if(o==null || !(o instanceof SomeModel))
      return false;

    SomeModel m = (SomeModel)o;
    return compare(this.objectField, m.objectField)
        && compare(this.primitiveArray, m.primitiveArray)
        && (primitiveField == primitiveField)
        && usw;
  }
```
Dadurch spart man sich das ständige prüfen von "null" Werten etc.

Mir geht es nur um die "primitive" Arrays, ob es einen Weg gibt diese nicht
zu casten.
Ich möchte in den equals Methoden keine Unterscheidung machen, was für Daten 
verglichen werden. Hauptsache der Vergleich liefert korrekte Ergebnisse.

Noch jemand mit Vorschlägen?


----------



## Isaac (19. Jul 2004)

Wenn du weist das die Objekte A und B vom gleichen Typ sind wieso überlädst du die compare Methode nicht einfach so oft wie du es brauchst?


```
private static boolean compare(Object a, Object b)
    private static boolean compare(int i,int j)
    private static boolean compare(double i,double j)
    private static boolean compare(char i,char j)
    private static boolean compare(byte i,byte j)      
    private static boolean compare(long i,long j)

    und was auch immer....
```


----------



## Guest (19. Jul 2004)

Du hast recht, ist noch die beste Lösung.

Irgendwie ist die Typenprüfung von Java bescheuert.
Es ist möglich z.B. ein char[] als Objekt zu übergeben,
man kriegt daraus auch die Information, dass es ein
Array ist und die Elemente "primitive" und auch den Typen 
der Elemente, dennoch gibt es keinen Sprachkonstrukt, diese
in ein Object-Array zu casten 
(mit einer automagischen Konvertierung in den jeweiligen 
Wrapper-Typen, hier Character).

OK, das war an sich kein Problem, nur den Code mit den vielen
if's fand ich ehmm.. uncool. 
Außerdem wird jedes typ.class zu Class.forName("typ"), was 
u.U. lamm sein kann, wenn man große Listen von Objekten 
durchsucht.

Gruß,
Michael


----------



## Isaac (19. Jul 2004)

Mit 1.5 geht das. Aber sollte man, laut SUN sparsam verwenden. Ist auch eigentlich nicht wirklich nötig aber macht den Source Code wieder ein bischen schmaler.


Was hast du denn gegen das überladen der Methoden? Das ist der Weg den man eigentlich immer gehen sollte. Der Code ist schmal und wartungsfreundlich. Im gegensatz zu den "one for all" Methoden. Das war ja ein Grund wieso man diese Überladung irgendwann man als nützlich eingeführt hat.

Schau doch alleine mal System.out.println ich glaube die ist irgendwas bei 15fach überladen, ohne nun gezählt  zu haben.


----------



## semi (19. Jul 2004)

OK, jetzt geht's weiter mit meinem Nicknamen. Habe vorhin das Passwort vergessen :wink:

Klar, so habe ich es letztendlich auch gemacht (nur bei Arrays char[], int[] etc.),
nicht für die "primitive" Typen wie char, int etc. 
Bei denen kann man auf den zusätzlichen Methodenaufruf verzichten (Klartext: a == b)

Es gibt noch zig andere Sachen, über die man sich in Java aufregen könnte 
(z.B. die Implementierung von java.util.AbstractList.indexOf)

Angenommen man hat ein Objekt, welches über irgendeine Integer-ID identifizierbar 
ist (oder auch PrimaryKey in EJB).

```
class A ...
{
  private Integer id;
  ...
}
```
Dazu eine equals-Methode, die auch die ID, also Integer, akzeptiert

```
public boolean equals(Object o)
  {
    if(o==null)
      return false;
    if(o instanceof A)
    {
      Cast und normaler Vergleich für alle Attribute
    }
    // ID des Objektes mit dem gegebenen Integer vergleichen
    if(o instanceof Integer)
    {
      return id.intValue()==((Integer)o).intValue();
    }
    return false;
  }
```
Nun packt man zig Objekte vom Typ A in z.B. ein ArrayList
und möchte die Position des Objektes mit der ID = 1 holen.
	
	
	
	





```
int index = arrayList.indexOf(new Integer(1));
```
Funzt nicht, da die indexOf-Methode in AbstractList wie folgt implementiert ist.
	
	
	
	





```
public int indexOf(Object o) {
	ListIterator e = listIterator();
	...
	    while (e.hasNext())
		if (o.equals(e.next()))
		    return e.previousIndex();
	}
	return -1;
    }
```
Der Vergleich entspricht also _Integer.equals(A)_, was verständlicherweise immer scheitert.
Wäre der Vergleich umgekehrt, 
	
	
	
	





```
e.next().equals(o)
```
dann könnte man durch eigene Implementierung der equals-Methode (wie oben)
selbst entscheiden, wann zwei Objekte gleich sind. Im Beispiel _A.equals(Integer)_
Auf die gleiche Art könnte man z.B. bei EJB schnell die Entity finden, die
in einer Liste lokaler Beans einen bestimmten Primary-Key hat.

Wie auch immer, genug gejammert 

Gruß,
Michael


----------



## Isaac (20. Jul 2004)

Das hab ich nun nicht wirklich verstanden. 

Object o und Object e sind doch sowieso vom gleichen Typ Die equals Methode der beiden Objekte sind also auch die gleichen und diese kannst du doch überschreiben. Beide implementierungen sollten doch dann auf das selbe hinausführen. Oder was bedenke ich dabei im Moment nicht?

Gruss,
Isa


----------



## Guest (20. Jul 2004)

Ich habe Vergleiche von Objekten gemeint, die unterschiedliche Typen haben.
Das folgende Beispiel verdeutlicht das "Problem".

```
public class Test
{
  public static void main(String argv[])
  {
    class Person
    {
      Integer id;
      String name;
      public Person(Integer id, String name)
      {
        this.id = id;
        this.name = name;
      }
      public boolean equals(Object o)
      {
        if(o == null)
          return false;
        if(o instanceof Person)
        {
          return this.id.equals(((Person)o).id);
        }
        // Hier der Vergleich der ID mit Integer 
        else if(o instanceof Integer)
        {
          return this.id.equals((Integer)o);
        }
        else 
          return false;
      }
    }

    Person p = new Person(new Integer(1), "Bart");
    System.out.println("Person.equals(Integer): " + p.equals(new Integer(1)));
    System.out.println("Integer.equals(Person): " + new Integer(1).equals(p));
  }
}
```
Dadurch, dass in AbstractList.indexOf(...) die zweite Variante ausgeführt wird,
kann man nicht Objekte mit unterschiedlichen Typen abfragen.
Stattdessen muss man selbst iterieren und über Getter die entsprechenden
ID's mit dem übergebenen Integer vergleichen.
Schau Dir mal die Implementierung von AbstractList.indexOf(...) an.
Es wird equals des übergebenen Objektes mit allen Elementen von
AbstractList verglichen.

OK, das ganze wird zu philosofisch. 
Ma' kann da nix mache', ischt halt so.

Gruß,
Michael


----------

