List<String> list =newArrayList<String>();try{while((buffer = ident_event.readLine())!=null){
list.add(buffer);}}catch(IOException e){
e.printStackTrace();}
Eine Datei ein, von der ich die Länge vorher nicht kenne. In der Datei stehen untereinander String-Paare:
Fahrrad Pedale
Moped Fußraste
usw.
Nun kann ich mit:
Java:
String[] ie_Array = list.toArray(newString[0]);
aus der Liste ein 1D Array machen.
Ich brauche aber ein 2D Array, also die Zeilen noch am Leerzeichen getrennt.
Mir fehl jede Idee, wie ich das anstelle, ohne jetzt das 2D Array zusätzlich zu erzeugen und das dann per Schleife zu befüllen.
Kann mir da wer helfen?
Gruß Jörg
Wird nicht ohne gehen. Vermutlich gibt es irgendeine Stream-Magie dafuer, aber eine einfache Schleife dafuer ist schnell getippt und leicht verstaendlich. Unmittelbare Array-Operationen fuer einen solchen Fall gibt es nicht.
per map dann das String.split aufrufen und schon hat man ein Stream von String Arrays
Stream zu Array ist doof … dann einfach erst zu List und dann zum Array. Also erst Stream mit toList abschließen, Zielatray erstellen und dann toArray auf der List aufrufen mit neuem Array als Parameter.
Ein durchaus typisches Beispiel für Streams mit gut lesbarem Ergebnis (finde ich)
Ich würde mal einfach behaupten, dass das, wonach gefragt wurde "ArrayList in 2D-Array konvertieren." gar nicht das ist, was eigentlich getan werden sollte.
Die Frage ist vermutlich eher: "Wie deserialisiere ich eine zeilenweise Textdatei bestehend aus Zeilen 'string1 string2' in eine passende Datenstruktur?"
Und die Antwort wäre: Baue dir eine Klasse oder Record, die zwei Felder hat (z.B. Fahrzeug und Bauteil) und lese Zeile für Zeile ein, splitte am Leerzeichen und erstelle eine java.util.List gefüllt mit Instanzen dieser Klasse/Record.
Denn weder ist die ursprüngliche Form der Information eine ArrayList (sie ist eine Datei), noch ist vermutlich das Konvertieren in ein 2D Array hier sinnvoll.
Erstmal vielen Dank für eure Antworten, die aber vom Level etwas zu hoch sind. Ich hab sie nicht verstanden.
Allerdings habe ich das Problem gelöst und packe diese hier mal hin.
Ist das so eine saubere Sache?
Ich würde sagen: Nein. Wenn es funktioniert, okay. Aber "sauber" im Sinne von "es erfüllt die Anforderungen mit der geringstmöglichen Komplexität" dann: Nein.
Es ist aber auch schwer zu bewerten, ob das jetzt eine "saubere" Lösung ist, weil wir deine genauen Anforderungen nicht kennen.
Also, allem Anschein nach (was dein zuletzt geposteter Code tut) willst du ja lediglich die Zeilen in der Datei formatiert ausgeben, also mit "Account: " und mit "*.evdata: ". Plus am Ende noch die Anzahl der Zeilen. Und danach vergisst du ja sowohl die ArrayList als auch das Array.
Wenn du lediglich nur diese Informationen auf der Konsole ausgeben willst, brauchst du weder eine ArrayList, noch ein Array.
Vielleicht sagst du jetzt aber auch: "Ja, das ist nur ein Beispiel und dieser Code soll sich woanders reinintegrieren, wo ich dann doch wieder das 2D-Array später brauche."
Ja genau, die Ausgabe dient nur der Kontrolle.
Die Daten werden später noch benötigt.
Wenn da also kein offensichtlicher Fehler drin ist, dann lass ich das jetzt so, denn weil ich von allein drauf gekommen bin, ist das die Art die ich verstehe.
Nein, zumindest der Versuch der Strukturierung des Codes mit Hilfe von Kommentaren ist eine ganz schlechte Idee (dafür verwendet man z. B. Methoden), ebenso wie der Verzicht auf try-with-resources.
Wenn da also kein offensichtlicher Fehler drin ist, dann lass ich das jetzt so, denn weil ich von allein drauf gekommen bin, ist das die Art die ich verstehe.
Wenn Du die Daten später noch einmal benötigst, ist der Code nicht unmittelbar verwendbar.
Was Du machen könntest wäre, dass Du Dir erstmal einen eigenen Datentyp (Klasse/Record) schreibst, der die Daten strukturiert speichert:
Java:
record AccountEvent(String account,String evdata){}
Jetzt kannst Du sagen: hey, lies mir mal alle Zeilen aus der Datei, füge jede Zeile als AccountEvent zu einer Liste hinzu und gib mir die Liste zurück. Das kann dann so aussehen:
Java:
List<AccountEvent>readAccountEventsFromFile(String filename)throwsIOException{// try-with-resources -> schließt die Datei automatischtry(BufferedReader reader =newBufferedReader(newFileReader(filename)))){List<AccountEvent> eventList =newArrayList<String>();String line;while((line = reader.readLine())!=null){// lies alle Zeilen aus der DateiaddAsAccountEvent(eventList, line);// füge jede Zeile als AccountEvent zur Liste eventList hinzu}// undreturn eventList;// gib mir die Liste zurück}}
Fehlt natürlich noch die Methode addAsAccountEvent:
Java:
privatevoidaddAccountEvent(List<AccountEvent> events,String line){AccountEvent event =accountEventFromLine(line);// mach mir aus der Zeile ein AccountEvent// ein bisschen Fehlerbehandlungif(event !=null){// und wenn das geklappt hat,
events.add(event);// dann füge das Objekt zur Liste hinzu.}}
Fehlt wieder eine Methode... accountEventFromLine:
Java:
privateAccountEventaccountEventFromLine(String line){int indexOfSpace = line.trim().indexOf(" ");// wir trimmen die Zeile, um keine bösen Überraschungen zu erhalten// ein bisschen Fehlerbehandlungif(indexOfSpace ==-1){// wenn kein Leerzeichen enthalten,returnnull;// enthält die Zeile keine vernünftigen Daten.// Ich gebe hier einfach mal null zurück}String account = line.substring(0, indexOfSpace);String evdata = line.substring(indexOfSpace +1);// da line getrimmt wurde, MUSS es weiterereturnnewAccountEvent(account, evdata);}
Das sieht jetzt erstmal viel umständlicher aus. Tatsächlich sind das jetzt aber nur drei Methoden, die eine klare Aufgabe haben. Außerdem ist der Code robust ggü. Zeilen ohne Leerzeichen bzw. Zeilen, die nur ein Leerzeichen am Zeilenende haben.
Man kann die Methode readAcountEventsFromFile nun allerdings auch kürzer schreiben:
Java:
List<AccountEvent>readAccountEventsFromFile(String filename)throwsIOException{returnFiles.lines(Paths.get(filename))// gib mir einen Stream der Zeilen aus der Datei filename.map(this::accountEventFromLine)// bilde jedes Element (=Zeile) mit Hilfe der accountEventFromLine Methode auf ein AccountEvent ab..filter(Objects::nonNull)// verwerfe alle Elemente, die null sind.toList();// und gib mir den Spaß gesammelt in einer Liste zurück}
Hallo,
mih7, ich danke die, für die Zeit und Arbeit die du dir gemacht hast, echt klasse.
Aber am Ende hast du mir nur gezeigt, was ich alles nicht kann :-(
Habe mir deinen Code jetzt ausgiebig angesehen und glaube das so einigermaßen nachvollziehen zu können- auch wenn ich das selber jetzt so immer noch nicht hinbekommen würde.
Ein Array kann ich mir bildlich vorstellen, in meinem Fall eine Tabelle mit 2 Spalten und benötigt viele Zeilen und ich kann darauf zugreifen, indem ich Zeile und Spalte angebe.
Bei deinem Code habe ich kein Bild und kann mir auch nicht vorstellen, wie ich jetzt an die einzelnen Werte komme.
Bin grad etwas deprimiert .....
Was ist ein Array? Zunächst einmal ist in Java ein Array einfach ein Speicher für eine gewisse Anzahl von Elementen eines bestimmten Typs. Ein zweidimensionales Array ist also nichts anderes als ein Array dessen Elemente vom Typ Array (eines bestimmten Typs) sind. Der Zugriff auf eine bestimmte Stelle im Array erfolgt über den Index-Operator (eckige Klammern).
Du kannst zum Beispiel ein String-Array erzeugen, das aus zwei Elementen besteht. Und Du kannst ein Array erzeugen, das aus 100 solcher String-Arrays besteht. In dem Fall hättest Du bildlich eine Tabelle mit 100 Zeilen und 2 Spalten.
So weit, so gut. Der Ansatz hat allerdings entscheidende Nachteile.
Wir wollen möglichst Code erhalten, den man gut lesen und schnell verstehen kann. Wenn wir Code vor uns haben, möchten wir Dinge wie "Kunde", "Adresse", "Rechnung", "Rechnungspositionen" usw. sehen und nicht irgendwelche x-elementigen Arrays, womöglich noch mit bescheidenen Bezeichnern und int-Literalen als Indizes, zumal der Typ für alle Elemente eines Arrays ja immer der selbe ist.
Nehmen wir mal eine Rechnung als Beispiel, die enthält einen Kunden, mehrere Rechnungspositionen, ein Rechnungsdatum usw. Versuch das mal in ein Array zu packen. Da würdest Du eine Krücke nach der anderen einbauen - der Code wird immer schlimmer. Das macht Dein Hirn ein paar Stunden mit, bevor es aussteigt.
Leider hast Du kaum Infos gegeben, was genau Deine Datei enthält, daher habe ich mich auf account und evdata beschränkt. Statt eines zwei-elementigen String-Arrays habe ich nun einen eigenen Datentyp für einen solchen Eintrag angelegt. Das ist der Record AccountEvent. Wie üblich können mit dem new-Operator Instanzen dieses Typs (sprich: Objekte) erzeugt werden.
Die Bezeichner für die Methoden leitet Java automatisch aus der Record-Definition ab:
Java:
record AccountEvent(String account,String evdata){}
Hätte ich dort String x, String y geschrieben, würde der Zugriff auf die Daten über die Methoden x() und y() erfolgen. Das macht Java für uns automatisch, daher habe ich auch einen record verwendet -> weniger Tipparbeit.
Natürlich kannst Du statt eines records auch eine eigene Klasse schreiben - ist halt mehr Arbeit. Allerdings ist ein Record immutable. Du kannst account und evdata im Nachhinein also nicht auf einen anderen Wert setzen. Mit einer eigenen Klasse wäre das natürlich möglich.
Statt mit event[0] und event[1] hat man jetzt also schön event.account() und event.evdata() im Code stehen.
Mit dem Typ könnten wir nun statt new String[100][2] schon einmal new AccountEvent[100] schreiben, was schon einmal eine erhebliche Verbesserung darstellt: events[0].account() ist wesentlich aussagekräftiger als events[0][0].
Allerdings wollen wir kein AccountEvent-Array, weil ein Array nur eine vorab definierte Anzahl an Elementen aufnehmen kann. Wir wollen eine Liste von AccountEvent-Objekten, die dynamisch in ihrer Größe ist. Eine Liste ist ein "abstrakter Datentyp", d. h. es ist bekannt, welche Operationen auf einer Liste ausgeführt werden können, und was sie im Ergebnis bewirken, die konkrete Implementierung ist allerdings offen, d. h.
Java:
List<AccountEvent> events;
besagt nur, dass events eine Liste von AccountEvent-Objekten darstellt, jedoch nicht, welche Implementierung verwendet wird. Welche man wählt, hängt von den konkreten Umständen ab, meist ist ArrayList aber nicht die schlechteste Wahl - insbesondere, wenn man sie statt eines Arrays verwenden will.
Java:
List<AccountEvent> events =newArrayList<>();
erzeugt nun ein neues ArrayList-Objekt, das Elemente vom Typ AccountEvent in einer Liste verwaltet. Welche Methoden List anbietet, kann man der Dokumentation entnehmen. Um ein Element in der Liste auszulesen, verwendet man die Methode get, die Methode set wird verwendet, um ein Element der Liste zu setzen. Hinzugefügt werden Elemente mit add, entfernt mit remove. Die Anzahl der in der Liste gespeicherten Elemente erhält man mit size.
Um z. B. jedes Element der Liste auszugeben, kann man beispielsweise schreiben:
Java:
for(int i =0; i < events.size(); i++){System.out.println(events.get(i));}
Natürlich funktioniert hier auch die for-each-Schleife. Außerdem hat List eine forEach-Methode usw.
Konkret auf den Code oben bezogen bedeutet dies, dass Du mit
Oh man, du hast dir so viel Arbeit gemacht ... vielen vielen Dank.
Ich bekomme aber schon bei
Java:
record AccountEvent(String account,String evdata){}
den Fehler : record can not be resolved to a type
Gefunden habe ich dazu, das der Class-Path eingetragen werden muß.
"Add ClassFolder" und dann das Verzeichnis "bin" auswählen.
Nur ... das gibt es nicht.
Wie aber schon mehrfach erwähnt, kannst Du auch eine Klasse schreiben, hier mal das Äquivalent zum record (aus Faulheitsgründen per ChatGPT generiert):
Java:
publicclassAccountEvent{privatefinalString account;privatefinalString evdata;publicAccountEvent(String account,String evdata){this.account = account;this.evdata = evdata;}publicStringgetAccount(){return account;}publicStringgetEvdata(){return evdata;}// Implementierung der hashCode-Methode@OverridepublicinthashCode(){int result =17;
result =31* result + account.hashCode();
result =31* result + evdata.hashCode();return result;}// Implementierung der equals-Methode@Overridepublicbooleanequals(Object obj){if(this== obj){returntrue;}if(obj ==null||getClass()!= obj.getClass()){returnfalse;}Record other =(Record) obj;return account.equals(other.account)&& evdata.equals(other.evdata);}// Implementierung der toString-Methode@OverridepublicStringtoString(){return"Record{"+"account='"+ account +'\''+", evdata='"+ evdata +'\''+'}';}}
Jetzt sehe ich gerade, dass ChatGPT Mist gebaut hat: die Methoden müssten account() und evdata() und nicht getAccount() und getEvdata() heißen. Dann kommen wir dem record schon ziemlich nah. Die toString()-Methode liefert auch nicht exakt das, was ein record liefert, aber das ist mal Nebensache.
Das ist zwar völlig OT, aber ich muss sagen, dass ich zu doof für ChatGPT bin. Zumindest habe ich es noch nie (kann mich jedenfalls nicht daran entsinnen) fertiggebracht, dem Teil wirklich Code zu entlocken, den ich haben möchte bzw. unmittelbar nutzen kann.
Dieser Punkt wird kontrovers diskutiert. Ich bin für die alte Schreibweise: getAccount() --- das neue records Namensschema ist falsch. Lässt sich jetzt aber nicht mehr revidieren, da es sonst Legacy-Code gäbe. 🤷♂️ nobody is perfect.
Mag sein, der Fehler aber ist, dass die Methoden in records nun einmal xyz() und nicht getXyz() heißen. Wenn ich also nach dem class-Äquivalent zum record frage, dann erwarte ich schon, dass ich auch eine äquivalente Klasse bekomme.
Es wurde doch nun schon gesagt das du keinen record mit Java 8 machen kannst da es denn da noch nicht gibt .
Also Alternative die dir schon gegeben wurde.
Natürlich kannst Du statt eines records auch eine eigene Klasse schreiben - ist halt mehr Arbeit. Allerdings ist ein Record immutable. Du kannst account und evdata im Nachhinein also nicht auf einen anderen Wert setzen. Mit einer eigenen Klasse wäre das natürlich möglich.
Gerne! Hier ist eine Datenklasse namens "DataRecord" mit zwei finalen String-Attributen, die auch die equals()- und hashCode()-Methoden überschreibt:
Java:
publicclassDataRecord{privatefinalString attribute1;privatefinalString attribute2;publicDataRecord(String attribute1,String attribute2){this.attribute1 = attribute1;this.attribute2 = attribute2;}publicStringgetAttribute1(){return attribute1;}publicStringgetAttribute2(){return attribute2;}@Overridepublicbooleanequals(Object o){if(this== o)returntrue;if(o ==null||getClass()!= o.getClass())returnfalse;DataRecord that =(DataRecord) o;if(!attribute1.equals(that.attribute1))returnfalse;return attribute2.equals(that.attribute2);}@OverridepublicinthashCode(){int result = attribute1.hashCode();
result =31* result + attribute2.hashCode();return result;}}
In dieser Klasse wurden die Attribute attribute1 und attribute2 als final deklariert, sodass sie nach der Initialisierung nicht mehr geändert werden können. Die equals()- und hashCode()-Methoden wurden überschrieben, um den Wertvergleich und die Hash-Funktion für Objekte der Klasse DataRecord zu implementieren.
Hier ist ein Beispiel, wie Sie diese Klasse verwenden können:
Java:
publicclassMain{publicstaticvoidmain(String[] args){DataRecord record1 =newDataRecord("Wert 1","Wert 2");DataRecord record2 =newDataRecord("Wert 1","Wert 2");DataRecord record3 =newDataRecord("Anderer Wert","Noch ein anderer Wert");// Überprüfen der Gleichheit mit equals()System.out.println("record1 und record2 sind gleich: "+ record1.equals(record2));// Ausgabe: trueSystem.out.println("record1 und record3 sind gleich: "+ record1.equals(record3));// Ausgabe: false// Hash-CodesSystem.out.println("Hash-Code von record1: "+ record1.hashCode());System.out.println("Hash-Code von record2: "+ record2.hashCode());System.out.println("Hash-Code von record3: "+ record3.hashCode());}}
Beachten Sie, dass das Überschreiben von equals() und hashCode() wichtig ist, um die korrekte Verwendung von Datenklassen in Collections wie HashMap oder HashSet zu gewährleisten.
Mein Fehler. ChatGPT hatte eine Klasse "Record" erstellt, ich habe die im Nachhinein zu AccountEvent umbenannt. Also: überall, wo "Record" (Großbuchstabe am Anfang) steht, durch AccountEvent ersetzen. Bzw die Klasse DataRecord von @lonLat verwenden
Um zwei Hash-Codes in Java zu kombinieren, können Sie die Methode Objects.hash() aus der Java Standardbibliothek verwenden. Diese Methode akzeptiert eine beliebige Anzahl von Argumenten und kombiniert die Hash-Codes dieser Argumente.
Hier ist ein Beispiel, wie Sie zwei Hash-Codes kombinieren können:
In diesem Beispiel wurden die Hash-Codes der beiden Strings "Hello" und "World" durch Aufruf der hashCode()-Methode erhalten. Anschließend wurden diese beiden Hash-Codes durch den Aufruf von Objects.hash() kombiniert, um einen einzelnen kombinierten Hash-Code zu erhalten.
Bitte beachten Sie, dass das Kombinieren von Hash-Codes auf diese Weise eine einfache Möglichkeit ist, aber es ist nicht immer perfekt. In einigen Fällen kann es zu sogenannten Hash-Kollisionen kommen, bei denen unterschiedliche Kombinationen von Werten denselben Hash-Code ergeben. In komplexeren Szenarien, wie beim Erstellen von Hash-Codes für Datenobjekte, kann es sinnvoll sein, eine spezielle Hash-Funktion zu implementieren, um solche Kollisionen zu minimieren.
Vielen Dank euch allen, aber ich gebe auf.
Ich bin mir sicher, das dieser Betreff vielen anderen weiterhelfen wird.
Der Fehler kommt jetzt bei DataRecord.
Eigentlich habe ich seit Donnerstag Urlaub und sitze hier trotzdem.
Scheinbar ist das Thema zu viel gewollt.
Werde meinem Chef nach dem Urlaub meine Kapitulation bekanntgeben.
Ich entschuldige mich bei euch für die vertane Zeit.
Gruß Jörg
Lasse das mit dem ChatGPT siehst ja was daraus kommt. mache es selber.
Und wenn du die Anleitung von mihe7 durchgehen willst . Dann heißt die Klasse auch nicht DataRecord.
Wie gesagt ist das eine Klasse die auch in eine eigne Datei gehört.
Hier mal eine Version, die auch unter Java 8 laufen dürfte:
Java:
importjava.util.Objects;publicclassAccountEvent{privatefinalString account;privatefinalString evdata;publicAccountEvent(String account,String evdata){this.account = account;this.evdata = evdata;}publicStringaccount(){return account;}publicStringevdata(){return evdata;}// Implementierung der hashCode-Methode@OverridepublicinthashCode(){returnObjects.hash(account, evdata);}// Implementierung der equals-Methode@Overridepublicbooleanequals(Object obj){if(this== obj){returntrue;}if(obj ==null||getClass()!= obj.getClass()){returnfalse;}AccountEvent other =(AccountEvent) obj;return account.equals(other.account)&& evdata.equals(other.evdata);}// Implementierung der toString-Methode@OverridepublicStringtoString(){return"AccountEvent["+"account='"+ account +'\''+", evdata='"+ evdata +'\''+']';}}
Java:
importjava.io.*;importjava.util.*;publicclassTest{publicstaticvoidmain(String[] args)throwsException{Test test =newTest();List<AccountEvent> events = test.readAccountEventsFromFile("ident_event.ini");for(AccountEvent event : events){System.out.println("Konto: "+ event.account()+", evdata: "+ event.evdata());}}publicList<AccountEvent>readAccountEventsFromFile(String filename)throwsIOException{// try-with-resources -> schließt die Datei automatischtry(BufferedReader reader =newBufferedReader(newFileReader(filename))){List<AccountEvent> eventList =newArrayList<>();String line;while((line = reader.readLine())!=null){// lies alle Zeilen aus der DateiaddAsAccountEvent(eventList, line);// füge jede Zeile als AccountEvent zur Liste eventList hinzu}// undreturn eventList;// gib mir die Liste zurück}}privatevoidaddAsAccountEvent(List<AccountEvent> events,String line){AccountEvent event =accountEventFromLine(line);// mach mir aus der Zeile ein AccountEvent// ein bisschen Fehlerbehandlungif(event !=null){// und wenn das geklappt hat,
events.add(event);// dann füge das Objekt zur Liste hinzu.}}privateAccountEventaccountEventFromLine(String line){int indexOfSpace = line.trim().indexOf(" ");// wir trimmen die Zeile, um keine bösen Überraschungen zu erhalten// ein bisschen Fehlerbehandlungif(indexOfSpace ==-1){// wenn kein Leerzeichen enthalten,returnnull;// enthält die Zeile keine vernünftigen Daten.// Ich gebe hier einfach mal null zurück}String account = line.substring(0, indexOfSpace);String evdata = line.substring(indexOfSpace +1);// da line getrimmt wurde, MUSS es weiterereturnnewAccountEvent(account, evdata);}}
Ich vermute eher, dass er es nicht geschafft hat, den Code (richtig) zu kopieren ... dann wird es mit dem Programmieren schwer. Er ist mit anderen Aufgaben sicherlich besser dran.
Mag sein, der Fehler aber ist, dass die Methoden in records nun einmal xyz() und nicht getXyz() heißen. Wenn ich also nach dem class-Äquivalent zum record frage, dann erwarte ich schon, dass ich auch eine äquivalente Klasse bekomme.
Hier ist evtl. auch das Problem, dass er Code generieren will, der sich an die coding guidelines hält. Ich selbst finde es etwas unglücklich, dass bei Records auf das get verzichtet wurde.
Aber so Dinge hatten wir hier ja auch schon öfters im Forum angesprochen, denn so Dinge findet man durchaus öfters.
Aber man will ja nicht meckern sondern sich einfach nur freuen, dass es Records gibt
Ich nutze Records inzwischen auch gerne, einfach, weil es z B Tipparbeit (Boilerplate-Code) spart.
Dennoch sollte man ja das Verständnis dafür erlangen (vielleicht gerade am Anfang), wie Records intern umgesetzt wurden. Zumindest würde ich mir das wünschen.
Ich denke das wir die records erwas der Koltin Sprache zuverdanken haben.
Denn da gehört die data Klasse einfach dazu. und da wird auch nicht mit einen vorangestelten get auf Properties zugegriffen.