[Java] Conversie van Container naar juist getypeerde array

Pagina: 1
Acties:

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 22-08 01:56
Ik vroeg me af of er een handige manier is, om een Container-object (een Vector bijvoorbeeld) met daarin een aantal objecten van een specifiek type, om te zetten naar een array van dat specifieke type.

De situatie waarin ik tegen dit probleem aanloop, is waarin ik een configuratiebestand inlees en (simpel gezegd) per regel een object aan wil maken. Omdat ik van te voren niet weet hoeveel objecten er gaan komen, voeg ik ze uiteraard toe aan een Container-object (een Vector, meer specifiek). Ik moet de resulterende verzameling van objecten echter doorgeven aan een constructor die een array van die objecten verwacht. Hoe doe ik dat op een handige, efficiënte en leesbare manier?

Mijn probleem wordt geïllustreert door de volgende code:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.Vector;

public class Test
{
    public static void main()
    {
        Vector vector = new Vector();

        vector.add(new Test());
        vector.add(new Test());
        vector.add(new Test());

        Test[] array = (Test[])vector.toArray(); // mag niet!
    }
}

De cast naar Test[] gooit een ClassCastException, aangezien toArray() een resultaat van het type Object[] oplevert. De hierarchy van Java array-typen is covariant met de typen die in de arrays zitten (dat wil zeggen: X[] is een subtype van Y[] (uitsluitend) wanneer X een subtype van Y is), dus het lijkt me niet onlogisch dat het type van een array naar een beperkter type gecast kan worden, wanneer dat dat met het type dat er in zit kan. Mocht dit at-runtime niet blijken te kunnen (wat in mijn geval gegarandeerd niet voorkomt) dan zou er een soort variatie op de bestaande ArrayStoreException gegooit kunnen worden.

Ik kon echter met mijn beschikbare Java-kennis en Google geen geschikte constructie vinden, om dit te doen. Mijn work-around is het construeren van een nieuwe array en het gebruik van een for-lusje om de boel heen en weer te kopiëren. Zo dus:
Java:
1
2
3
Test[] array = new Test(vector.size());
for(int n = 0; n < array.length; ++n)
    array[n] = (Test)vector.get(n);

Maar dit is uiteraard erg inefficient en slecht leesbaar bovendien. Heeft iemand een beter idee? Of is dit het best haalbare? De bestaande constructor die een array verwacht wijzigen wil ik niet, omdat de type safety die de array nu garandeerd dan komt te vervallen (een Collection, Iterator of Enumerator kan alleen Objecten opleveren) en bovendien is dit geen algemene oplossing.

  • Marcj
  • Registratie: November 2000
  • Laatst online: 09:50
1) Volgens mij is dit nauwelijks inefficienter dan de functie toArray() doet. Een vector moet het ook helemaal converten naar een array (alleen een ArrayList kon dit dacht ik heel efficient).
2) Java is niet gebouwd op efficientie :P
3) Slechte leesbaarheid is op te lossen door zelf een statische functie er voor te schrijven. Misschien nog niet zo mooi, maar ik denk wel beter dan wat er nu staat.

Er is dacht ik geen andere manier om dit op te lossen.

edit: wacht ff, deze al gelezen???
http://java.sun.com/j2se/...Array(java.lang.Object[])

[ Voor 14% gewijzigd door Marcj op 04-03-2003 00:31 ]


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 22-08 01:56
Pfff. Het is duidelijk dat ik weer te lang achter elkaar zit te werken. Ik had een mooie methode op de Collection interface over het hoofd gezien:
Object[] toArray(Object[] a)
Returns an array containing all of the elements in this collection; the runtime type of the returned array is that of the specified array.
Daarmee kan ik dus een array van het juiste type construeren en die meegeven. Het is nog steeds wat omslachtig, maar stukken beter dan de work-around. 't Wordt dan dus zoiets:
Java:
1
Test[] array = (Test[])vector.toArray(new Test[0]);

  • Marcj
  • Registratie: November 2000
  • Laatst online: 09:50
:D Tegelijkertijd gevonden ;)

Maar volgens mij moet het dan nog zijn:
Java:
1
Test[] array = vector.toArray(new Test[0]);


Die cast kan toch niet werken? Of misschien moet je het gewoon testen ;) Ik weet het ook niet zeker.

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 22-08 01:56
Marcj schreef op 04 maart 2003 @ 00:30:
1) Volgens mij is dit nauwelijks inefficienter dan de functie toArray() doet. Een vector moet het ook helemaal converten naar een array (alleen een ArrayList kon dit dacht ik heel efficient).
Intern heeft 'ie een array, dus hij hoeft de boel alleen maar te kopiëren als één groot blok geheugen. Ik moest elke keer een index ophogen en een runtime type check doen. Dat is wel degelijk minder efficient, al was dat niet zozeer een issue (het ging me meer om het principe dat 't een hoop gedoe is).

Een ArrayList is intern trouwens ongeveer hetzelfde als een Vector (volgens de documentatie) met het verschil dat een Vector synchronized is en ik 'm dus ten onrechte gebruik (moet maar eens een grote search-and-replace doen).
Mja, ik zag 't net. Beetje stom van me dat ik binnen zo'n korte tijd zelf de goede oplossing vind. Ik moet er de eerste keer overheen gelezen hebben. Toch bedankt! ;)

[ Voor 5% gewijzigd door Soultaker op 04-03-2003 00:35 ]


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 22-08 01:56
Marcj schreef op 04 March 2003 @ 00:33:
Maar volgens mij moet het dan nog zijn:
Java:
1
Test[] array = vector.toArray(new Test[0]);


Die cast kan toch niet werken? Of misschien moet je het gewoon testen ;) Ik weet het ook niet zeker.
Die cast moet er juist wél bij, aangezien Object[] geen subtype van Test[] is (maar omgekeerd wel). Enige verschil is nu dat het runtime type van het resultaat nu echt Test[] is en niet Object[], zoals eerst het geval was (waardoor ik 'm eerst niet, en nu wel, naar Test[] kon casten).

[ Voor 6% gewijzigd door Soultaker op 04-03-2003 00:37 ]


  • Marcj
  • Registratie: November 2000
  • Laatst online: 09:50
Soultaker schreef op 04 March 2003 @ 00:35:
[...]

Intern heeft 'ie een array, dus hij hoeft de boel alleen maar te kopiëren als één groot blok geheugen. Ik moest elke keer een index ophogen en een runtime type check doen. Dat is wel degelijk minder efficient, al was dat niet zozeer een issue (het ging me meer om het principe dat 't een hoop gedoe is).

Een ArrayList is intern trouwens ongeveer hetzelfde als een Vector (volgens de documentatie) met het verschil dat een Vector synchronized is en ik 'm dus ten onrechte gebruik (moet maar eens een grote search-and-replace doen).
Oeps ja.. ik ben in de war met een ander type. Maar zou je zowiezo niet gewoon de lijst definiëren als List? Dan kun je later gewoon het precieze type aanpassen om te kijken welke dan de beste keuze is :)

Dus dit:
Java:
1
Vector vector = new Vector();

vervangen met
Java:
1
List list = new Vector();

Verwijderd

...

[ Voor 98% gewijzigd door Verwijderd op 04-03-2003 07:08 ]


  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

(overleden)
[nohtml]
Soultaker schreef op 04 maart 2003 @ 00:35:
Een ArrayList is intern trouwens ongeveer hetzelfde als een Vector (volgens de documentatie) met het verschil dat een Vector synchronized is en ik 'm dus ten onrechte gebruik (moet maar eens een grote search-and-replace doen).
Ook volgens mij :) Een Vector is pre Java 2 en is eigenlijk niet ontworpen op de Collection/List interface. Je merkt ook dat er zut als addElement( ) nog in zit om legacy code te ondersteunen. Tevens kan een Vector ook nog Enummerations teruggeven :P Echter Vector is wel aangepast aan de List interface na Java 2, waardoor deze ook add( ) ed. heeft :)

Ik zou je ook willen aanraden iig ArrayList te gebruiken. ArrayList is idd niet syncronized en dat is net als de rest van de collections. Als je je List toch wilt syncronizen dan heb je daar een static method voor in Collections. Vector is dus overbodig (net als HashTable)

Verder vind ik het altijd een crime om met Java collections te werken. Je zit maar te casten en te casten :/ Wat je syntax ontzettend goor maakt. Jij als C++ coder moet met templates kunnen omgaan. Waarom gebruik je niet de Java pre-generics patch :) http://developer.java.sun...lyAccess/adding_generics/ en http://developer.java.sun...ticles/releases/generics/

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 22-08 01:56
Glimi schreef op 04 March 2003 @ 10:39:
Ik zou je ook willen aanraden iig ArrayList te gebruiken. ArrayList is idd niet syncronized en dat is net als de rest van de collections. Als je je List toch wilt syncronizen dan heb je daar een static method voor in Collections. Vector is dus overbodig (net als HashTable)
Ja, ik weet het wel, maar uit C++-gewoonte gebruik ik standaard een Vector als ik een simpele container nodig heb. |:( Beetje stom van me; dat wordt een grote search-and-replace actie.
Verder vind ik het altijd een crime om met Java collections te werken. Je zit maar te casten en te casten :/ Wat je syntax ontzettend goor maakt. Jij als C++ coder moet met templates kunnen omgaan. Waarom gebruik je niet de Java pre-generics patch :)
Ik vind werken met Java sowieso erg vervelend; ik moet helaas met anderen samenwerken die alleen Java begrijpen. Generics toevoegen elimineert dan het nut van Java gebruiken, aangezien ze 't dan niet meer begrijpen. :)

Als het aan mij had gelegen, hadden we direct C of C++ gebruikt.

Het bezwaar van dat casten is trouwens niet alleen dat je code slecht leesbaar wordt, maar ook dat je type checking van compile-time naar run-time verplaatst. Het gebeurd mij regelmatig dat ik een verkeerde cast neerzet (ook omdat ik soms stukjes code copy-paste) en dan krijg ik daar pas bij het runnen een foutmelding van. Niet alleen is het veel makkelijker als de compiler dat afvangt, maar er is ook geen harde garantie dat alle run-time type fouten ook gedetecteerd worden. Als ik een fout maak in een stuk code dat vrijwel nooit aangeroepen wordt (in een catch-clause, bijvoorbeeld) dan is de kans groot dat die fout nooit aan het licht komt.

[ Voor 23% gewijzigd door Soultaker op 04-03-2003 12:45 ]


  • KinkyClown
  • Registratie: Maart 2000
  • Laatst online: 13-06-2025

KinkyClown

Eenvoud is beter dan twee fout

Marcj had het bijna goed

Het is:
Java:
1
Test[] array = (Test[]) vector.toArray(new Test[0]);

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 22-08 13:19

.oisyn

Moderator Devschuur®

Demotivational Speaker

en dat had soultaker in die post daarboven dus al gezegd

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.

Pagina: 1