Obwohl Sie in Ihren Kommentaren "Schnelligkeit" erwähnen, ohne anzugeben, welche Operation "schnell" sein muss, scheint Ihr Hauptanliegen der Speicherverbrauch zu sein.
Nehmen wir an 100 Gruppen von 300.000 Nummern (Sie haben Wörter wie "vielleicht" und "weit über" verwendet, aber dies wird als ein Beispiel).
Das sind 30.000.000 zu speichernde Zahlen plus 100 Überschriften und ein struktureller Mehraufwand für die Gruppierung.
Ein primitives Java float
ist 32 Bits, das sind 4 Bytes. Also auf ein absolutes Minimum, werden Sie 30.000.000 * 4 Bytes == 120MB benötigen.
Ein Array von Primitiven - float[30000000]
- ist nur alle Werte in einem zusammenhängenden Stück Speicher verkettet, so wird dieses theoretische Minimum von 120MB verbrauchen - plus ein paar Bytes von einmal pro Array Overhead, die ich nicht werde gehe hier ins Detail.
Ein Java Float
Wrapper-Objekt ist 12 Bytes. Wenn Sie ein Objekt (anstatt eines Grundelements) in einem Array speichern, beträgt die Referenz selbst 4 Byte. Ein Array von Float
- Float[30000000]
wird also 30.000.000 * (12 + 4) == 480MB verbrauchen.
So können Sie die Speicherbelegung um mehr als die Hälfte reduzieren, indem Sie primitive statt Wrapper verwenden.
Ein ArrayList
ist ein ziemlich Licht Wrapper um ein Array von Object
und hat so zu den gleichen Speicherkosten. Die Overheads pro Listeneintrag sind bei diesen Listengrößen zu klein, um Auswirkungen gegenüber den Elementen zu haben. Aber es gibt einige Einschränkungen:
ArrayList
können nur Objekte speichern, nicht Primitiven, wenn Sie also eine List
wählen bist du mit dem stecken 12-Byte-pro-Element-Overhead von Float
.
- Die Kapazität eines
ArrayList
ist dynamisch, und um dies zu erreichen, wenn Sie die Liste wachsen größer zu sein als sein Array sichern, wird es:
- ein neues Array erstellen, 50% größer als das alte Array
- kopieren sie den Inhalt des alten Array in das neue Array (das klingt teuer, aber Hardware ist sehr schnell auf diese Weise)
- verwerfen die alte Array
- Dies bedeutet, dass, wenn die Träger Array 30 Millionen Elementen haben, geschieht, und ist voll,
ArrayList.add()
wird das Array mit einer von 45 Millionen Elemente, auch wenn Ihr List
braucht nur 30.000.001 zu ersetzen.
- Sie können dies vermeiden, wenn Sie die erforderliche Kapazität im Voraus kennen, indem Sie die Kapazität im Konstruktor bereitstellen.
- Sie können
ArrayList.trimToSize()
verwenden, um nicht benötigte Kapazität zu löschen und etwas Speicher zurückzunehmen, nachdem Sie die ArrayList
gefüllt haben.
Wenn ich so wenig Heap-Speicher wie möglich verwenden strebe, würde ich will meine Listen von Zahlen als Arrays von Primitiven speichern:
class Data {
String header;
float[] values;
}
... und Ich würde diese einfach in eine setzen.
Mit dieser Struktur haben Sie O (1) Zugriff auf beliebige Werte, und Sie können Arrays.binarySearch()
(wenn die Werte sortiert sind) nach Wert innerhalb einer Gruppe suchen.
Wenn möglich, würde ich die Größe jeder Gruppe vor dem Lesen der Werte herausfinden und das Array auf die richtige Größe initialisieren. Wenn Sie können, Ihre Eingabe-Dateiformat diese machen erleichtern:
while(line = readLine()) {
if(isHeader(line)) {
ParsedHeader header = new ParsedHeader(line);
currentArray = new float[header.size()];
arrayIndex = 0;
currentGroup = new Group(header.name(), currentArray);
groups.add(currentGroup);
} else if (isValue(line)) {
currentArray[arrayIndex++] = parseValue(line);
}
}
Wenn Sie nicht das Eingabeformat ändern kann, sollten zwei Durchgänge durch die Datei zu machen - einmal Gruppenlängen zu entdecken, noch einmal Ihre Arrays zu füllen.
Wenn Sie müssen die Datei in einem Durchgang konsumieren, und das Dateiformat kann keine Gruppenlängen vor Gruppen bieten, dann müssen Sie etwas tun, das eine "Liste" beliebig wachsen lässt. Es gibt mehrere Möglichkeiten:
jedoch nichts davon hält Ihre Gründe für die in erster Linie all diese Zahlen in den Speicher Schlürfen, noch, ob dieser Speicher Ihre Bedürfnisse erfüllt, wenn es darum geht, die Zahlen zu verarbeiten.
Sie sollten zurücktreten und überlegen, was Ihre eigentliche Datenverarbeitungsanforderung ist und ob das Einschlafen in den Speicher der beste Ansatz ist.
Sehen Sie, ob Sie Ihre Verarbeitung durchführen können, indem Sie nur eine Scheibe von Daten auf einmal speichern, anstatt die ganze Sache im Speicher zu speichern. Um beispielsweise Max/Min/Mittelwert zu berechnen, müssen Sie nicht jede Zahl im Speicher haben - Sie müssen nur eine laufende Summe behalten.
Oder überlegen Sie sich, eine leichtgewichtige Datenbankbibliothek zu verwenden.
Nun, da die Reihenfolge wichtig ist, verwenden Sie die Datenstrukturen, die die 'Queue'-Schnittstelle implementieren. Wie zum Beispiel 'LinkedList' –
Was ist los mit ArrayList? Brauchen Sie einen zufälligen Zugriff auf diese Elemente? –
Was meinst du mit "am effizientesten"? Die geringste Erinnerung aufnehmen? Am schnellsten zu schreiben? Am schnellsten zum sequenziellen Lesen? Am schnellsten zum Abrufen (nach welchen Kriterien?)? Was hast du bisher versucht, und warum glaubst du, dass es nicht "effizient" genug ist? – slim