Soms schrijf ik methodes die 2 dingen returnen. Een versimpeld voorbeeld, je hebt een deel (delen) operatie, die voor ints ook de rest terug geeft in 1 functie. Die is zo omdat je het efficient wilt doen.
Of je zoekt iets in een datastructuur, en als je het vind wil je terug geven het object dat gevonden is en een index. Of voor de iteratie over een map, met key value paren, dan heb je ook altijd 2 objecten.
En erg veel gevallen die je kan bedenken kan het zijn dat je meerdere waardes terug wilt geven. In sommige situaties loont het de moeite om daar een speciale klasse voor de schrijven, zoals Map.Entry, maar soms is dat best wel overkill.
Daarom heb ik voor Java (1.5) deze klassen geschreven:
Ik gebruik de getX methodes nooit, maar doe direct field accesses. De fields zijn perse final. Helaas kan je een field geen getal naam geven, misschien verander ik het nog "_1".
Eigenlijk vind ik het vreemd dat Java geen Pair class heeft. Ik heb ook nog een static method geschreven die 2 Lists in een List van Pairs maakt en andersom:
Tuples zijn niet efficient, vooral niet als ze maar kort bestaan of je er heel veel van hebt. Maar zolang je ze buiten tight loops houdt is het te overzien. Mocht zoiets toch voorkomen, kan je het altijd nog vervangen met een echte class. Dat scheelt de cast overhead in de VM, en de wrappers als je primitives gebruikt in je Tuples. Ik heb ook nog een Tuple4 en hogere getallen zijn erg makkelijk om te maken. Het grootste nadeel is dat je geen context informatie hebt in je Tuple objecten. Dus je weet wel wat de types van de waarden in je Tuples zijn, maar niet in welke context. Een String voor elke waarde in elke generic instance van een tuple zou handig zijn, maar ik zou niet weten hoe dat te coden.
Nu de vraag: wat doen jullie in dergelijke gevallen? Maken jullie altijd nieuwe wrapper classjes? Of roep je twee methodes aan voor de twee waardes?
Of je zoekt iets in een datastructuur, en als je het vind wil je terug geven het object dat gevonden is en een index. Of voor de iteratie over een map, met key value paren, dan heb je ook altijd 2 objecten.
En erg veel gevallen die je kan bedenken kan het zijn dat je meerdere waardes terug wilt geven. In sommige situaties loont het de moeite om daar een speciale klasse voor de schrijven, zoals Map.Entry, maar soms is dat best wel overkill.
Daarom heb ik voor Java (1.5) deze klassen geschreven:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| public class Tuple <A, B>{ public final A a; public final B b; public Tuple(A a, B b){this.a = a; this.b = b;} public A get1(){ return a; } public B get2(){ return b; } @Override public String toString() { return "<" + a.toString() + ", " + b.toString() + ">"; } } public class Tuple3<A, B, C> extends Tuple<A, B> { public final C c; public Tuple3(A a, B b, C c) { super(a, b); this.c = c; } public Tuple3(Tuple<A, B> t, C c){ super(t.a, t.b); this.c = c; } public C get3(){return c;} @Override public String toString() { return "<" + a.toString() + ", " + b.toString() + ", " + c.toString() + ">"; } } |
Ik gebruik de getX methodes nooit, maar doe direct field accesses. De fields zijn perse final. Helaas kan je een field geen getal naam geven, misschien verander ik het nog "_1".
Eigenlijk vind ik het vreemd dat Java geen Pair class heeft. Ik heb ook nog een static method geschreven die 2 Lists in een List van Pairs maakt en andersom:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| public static <A,B> List<Tuple<A,B>> zip(List<A> listA, List<B> listB){ if(listA == null || listB == null) throw new NullPointerException("Lists can't be null"); int length = listA.size(); if(listB.size() != length) throw new IllegalArgumentException("List sizes are not equal"); ArrayList<Tuple<A,B>> newList = new ArrayList<Tuple<A,B>>(length); if(listA instanceof RandomAccess && listB instanceof RandomAccess){ for(int i = 0; i < length; i++){ newList.add(new Tuple<A,B>(listA.get(i), listB.get(i))); } } else { Iterator<A> iter = listA.iterator(); for (B b : listB) { newList.add(new Tuple<A,B>(iter.next(), b)); } } return newList; } public static <A,B> Tuple<List<A>, List<B>> unzip(List<Tuple<A,B>> list){ ArrayList<A> listA = new ArrayList<A>(list.size()); ArrayList<B> listB = new ArrayList<B>(list.size()); for (Tuple<A,B> tuple : list) { listA.add(tuple.a); listB.add(tuple.b); } return new Tuple<List<A>, List<B>>(listA, listB); } |
Tuples zijn niet efficient, vooral niet als ze maar kort bestaan of je er heel veel van hebt. Maar zolang je ze buiten tight loops houdt is het te overzien. Mocht zoiets toch voorkomen, kan je het altijd nog vervangen met een echte class. Dat scheelt de cast overhead in de VM, en de wrappers als je primitives gebruikt in je Tuples. Ik heb ook nog een Tuple4 en hogere getallen zijn erg makkelijk om te maken. Het grootste nadeel is dat je geen context informatie hebt in je Tuple objecten. Dus je weet wel wat de types van de waarden in je Tuples zijn, maar niet in welke context. Een String voor elke waarde in elke generic instance van een tuple zou handig zijn, maar ik zou niet weten hoe dat te coden.
Nu de vraag: wat doen jullie in dergelijke gevallen? Maken jullie altijd nieuwe wrapper classjes? Of roep je twee methodes aan voor de twee waardes?
"Beauty is the ultimate defence against complexity." David Gelernter