2012-04-10 13 views
9

ich zuteilen eine große Auswahl an verdoppelt sich alsWie Initialisierung eines großen Arrays vermeiden

double[] x = new double[ n ]; 

wobei n groß ist, und ich möchte Initialisierung zu vermeiden, Zeit zu sparen. Ist es möglich?

+0

Java-Tag zu Ihrer Frage hinzufügen –

+1

Einstellung vm Argument Xms4000m hilft. Es wird jetzt in einer Minute initialisiert. –

+0

Ich kann es nicht bearbeiten, es ist nicht meine Frage. –

Antwort

10

Kurze Antwort: Nein. Arrays werden immer gelöscht, wenn sie erstellt werden.

Wenn Ihr Profiling gezeigt hat, dass dies ein erheblicher Engpass ist, könnten Sie einen Pool von Array-Instanzen mit einer Länge von jeweils größer als n in Betracht ziehen. Das Problem wäre, dass Sie dann wahrscheinlich ein Wrapper-Objekt benötigen, das das Daten-Array und die tatsächlich verwendete Länge enthält, da Sie data.length nicht mehr verwenden können.

10

Können Sie eine ArrayList oder etwas verwenden, und bauen Sie Ihr Array, wie Sie Elemente hinzufügen müssen? Das spart Initialisierungszeit, wenn das Ihr Problem ist.

ArrayList<double> x = new ArrayList<double>(); 
+0

Ich denke, das ist die beste Sache zu tun. Alle anderen Antworten erfordern eine Menge Problemumgehungen, die in Zukunft auch zu mehr Zeitproblemen führen können. Ich frage mich jedoch, gibt es eine Java-Bibliothek, die Arrays erstellt, wo Sie jeden Wert initialisieren müssen, anstatt mit Nullen automatisch zu füllen? – patrickhuie19

+6

@Nick Rolando Sie können ArrayList nicht für primitive Typen wie 'double' erstellen. Stattdessen müssen Sie 'ArrayList ' verwenden. Es wird Boxing/Unboxing für die Konvertierung vom primitiven Typ zum Referenztyp verwenden. Wenn man alles, was oben gesagt wurde, konsumiert, ist diese Antwort nicht die beste für das Autorenproblem. –

+1

Das ist eine ** schlechte Idee **. Die Liste durch das Hinzufügen von Elementen zu vergrößern, ist deutlich teurer, als sie zu initialisieren, um die richtige Größe zu haben. Wie oben erwähnt, können Sie auch keine 'ArrayList' von Primitiven haben, so dass dieser Ansatz auch eine erhebliche Speicherkosten verursachen kann, wenn die Anzahl der Elemente groß ist. – Dima

1

Sobald Sie die Anweisung "new double [n]" deklarieren, wird das Array initialisiert. Es gibt keinen Weg um ihn herum.

Wenn Sie dies aus Gründen der Optimierung tun, dann beschäftige ich Sie, um auf vorzeitige Optimierung zu lesen. Wenn Ihr Programm nicht auf eine Wand trifft, lohnt es sich nicht zu optimieren. Und es ist definitiv nicht das Array, das Sie auch optimieren sollten.

0

Sie können Arraylist verwenden, um Zeit zu sparen auf die Initialisierung und konvertiert sie dann in ein Array, wenn Sie absolut wie diese auf Double Array arbeiten müssen:

List<Double> withoutInitializing = new ArrayList<Double>(); 
Double[] nowYouConvert = (Double[]) withoutInitializing.toArray(); 

Von Java-Dokumentation:

toArray : Gibt ein Array zurück, das alle Elemente in dieser Liste in der richtigen Reihenfolge enthält (vom ersten bis zum letzten Element).

Das zurückgegebene Array wird "sicher" sein, da keine Referenzen darauf von dieser Liste verwaltet werden. (Mit anderen Worten, diese Methode muss ein neues Array zuweisen, auch wenn diese Liste von einem Array unterstützt wird). Der Aufrufer ist somit frei das zurückgegebene Array zu ändern.

Diese Methode fungiert als Brücke zwischen Array-basierten und sammlungbasierten APIs.

definiert durch: toArray() in Sammlung

+1

Das Problem dabei ist, dass der ArrayList-Zugriff etwas langsamer ist, was einen großen Unterschied in meinem Hochleistungsalgorithmus macht. –

+0

'ArrayList' wird tatsächlich von einem Array (von Objekten) unterstützt, anfänglich auf eine beliebige Konstante (' DEFAULT_CAPACITY = 10') mit dem Standardkonstruktor, und dann wird das Array jedes Mal neu zugewiesen, wenn kein Platz mehr zur Verfügung steht. Wenn Sie wirklich n Einträge speichern möchten, haben Sie am Ende O (log (n)) -Arrays angelegt, wobei die letzte mindestens die Größe (wahrscheinlich größer) als die Anzahl der Elemente hat, die zu der Liste hinzugefügt wurden Liste. Sie können die Neuzuweisung vermeiden, indem Sie die erforderliche Kapazität durch Aufrufen des spezialisierten Konstruktors oder durch Aufrufen von 'sureCapacity()' sicherstellen. –

+0

Das würde also keinesfalls das Erzeugen ** und Initialisieren ** eines großen Arrays vermeiden. Ganz zu schweigen davon, dass Double-Objekte doppelte Werte anstelle von direkten doppelten Werten in einem Array umhüllen, was im Hinblick auf die Speichernutzung sogar noch schlimmer ist, wenn man den Overhead des Wrapper-Objekts berücksichtigt. –

0

Einige Abhilfe für wie Array nicht initialisiert ist.

Erstellen Sie ein Array, das garantiert größer als die größtmögliche Anzahl an Einträgen ist, und füllen Sie es teilweise aus.

Zum Beispiel können Sie entscheiden, dass der Benutzer nie mehr als 100 Eingabewerte bereitstellen wird. Dann zuteilen eine Reihe von Größe 100:

final int VALUES_LENGTH = 100; 
double[] values = new double[VALUES_LENGTH]; 

dann einen Begleiter Variable halten, wie viele Elemente im Array erzählt tatsächlich verwendet werden.

int valuesSize = 0; 

Nun ist values.length die Kapazität der Feldwerte und valuesSize ist die aktuelle Größe des Arrays. Fügen Sie Elemente in das Array ein und erhöhen Sie die Variable valuesSize jedes Mal.

values[valuesSize] = x; 
valuesSize++; 

Auf diese Weise enthält valuesSize immer das richtige Element zu zählen. Das folgende Codesegment zeigt, wie Zahlen in ein teilweise gefülltes Array gelesen werden.

int valuesSize = 0; 
Scanner in = new Scanner(System.in); 
while (in.hasNextDouble()) { 
    if (valuesSize < values.length) { 
    values[valuesSize] = in.nextDouble(); 
    valuesSize++; 
    } 
} 

Am Ende dieser Schleife enthält valuesSize die tatsächliche Anzahl von Elementen in dem Array.

Zum Beispiel, hier ist, wie Sie eine beliebig lange Sequenznummern in ein Array lesen können, ohne Raum aus ausgeführt wird:

int valuesSize = 0; 
while (in.hasNextDouble()) { 
    if (valuesSize == values.length) { 
     values = Arrays.copyOf(values, 2 * values.length); 
    } 
    values[valuesSize] = in.nextDouble(); 
    valuesSize++; 
} 
2

Wie andere bereits erwähnt haben, ist die einfache Antwort ist: nein, Sie sind nicht in der Lage, den Initialisierungsteil zu vermeiden. Es sei denn, Sie verwenden einige native Zuordnung oder IntBuffer erstellt als eine Ansicht von byte buffer wird direkt sein, wenn und nur wenn, der Byte-Puffer selbst ist direkt.

Wenn Sie keine von ihnen verwenden, müssen Sie, um das Array so schnell wie möglich zuzuordnen und zu initialisieren, die GC Aufrufe minimieren und sicherstellen, dass die JVM über den erforderlichen Speicher zum Speichern und Arbeiten verfügt dieses Array.

In Albert Hendriks Fall: static int[][] lookup = new int[113088217][2], ohne mindestens 2,3 G (12 + 113088217 * (12 + 2 * 4) Bytes) Speicher die JVM wird nicht in der Lage sein, den benötigten Speicherplatz zuzuweisen. Beachten Sie, dass ich den Füllraum (Speicherausrichtung), der auch benötigt wird, nicht hinzugefügt habe.

Um zu beantworten, warum lookup = new int[2*113088217]; schneller funktioniert. Es ist, weil viel weniger Speicher gehandhabt wird, da wir die Unterarrays nicht haben (der Header + die Elemente + die Ausrichtung für jedes Unterarray), nur (2 * 113088217 * 4 + 12) Bytes = ~ 804M werden benötigt.

5

Wenn Sie ein zu langes Array nicht initialisieren möchten, können Sie ein Limit für Ihre Array-Größe festlegen, wodurch Sie nicht zu lange warten müssen. Ich schlage vor, ein langes Array in kleinere Arrays aufzuteilen. Definieren Sie eine Liste, die Ihre Arrays enthält. Wenn Ihr Array gefüllt ist, fügen Sie es in Ihre Liste ein. Und füge ein neues hinzu.

import java.util.ArrayList; 
import java.util.List; 

public class Tester { 
    private static final int LIMIT = 30; 
    private static int index = 0; 

    private static int[][] lookup; 
    private static List<int[][]> list = new ArrayList<int[][]>(); 

    public static void main(String[] args) { 
     lookup = new int[LIMIT][1]; 

     for (int i = 0; i <= 93; i++) { 
      addToArr(i); 
     } 

     list.add(lookup); 

     for (int[][] intArr : list) { 
      for (int i = 0; i < intArr.length; i++) { 
       System.out.print(intArr[i][0] + ","); 
      } 
     } 
    } 

    public static void addToArr(int value) { 
     if (index == LIMIT) { 
      list.add(lookup); 
      lookup = new int[LIMIT][1]; 
      index = 0; 
     } 
     lookup [index++][0] = value; 
    } 
} 

Drucke:

0,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,31,32,33,34,35,36,37,38,39,40,41,42 , 43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67 68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92 , 93,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 0,0,

2

** WARNUNG ** UNSAFE ALTERNATIVE **

Es ist keine exakte Lösung, aber es könnte eine brauchbare Alternative sein. Diese Methode birgt gewisse Risiken. Aber Sie können diesen Weg gehen, wenn es wirklich absolut notwendig ist.Diese Methode verwendet eine undokumentierte sun.misc.Unsafe Klasse, um Off-Heap-Speicher zu reservieren, um die doppelten Werte zu speichern. Off-Heap bedeutet, dass es sich nicht um einen Müll handelt, daher müssen Sie darauf achten, den zugehörigen Speicher freizugeben.

Der folgende Code basiert auf diesem blog post about sun.misc.Unsafe.

Der folgende Code wird einige zufällige doppelte Werte aus dem unitialized-Speicher drucken.

SuperDoubleArray sda = new SuperDoubleArray(100); 
for (int i=0; i<sda.size(); i++) { 
    System.out.println(sda.get(i)); 
} 
sda.deallocate(); 

Es gibt keine Sicherheit/Entfernungs-Kontrollen, kein nichts, können Sie bequem die JVM mit ihm abstürzen, möglicherweise nicht mit Nicht-SUN JREs arbeiten, könnte sogar arbeiten in Zukunft SUN JRE-Versionen stoppen, aber es könnte in einigen Fällen die einzige Lösung sein. Es kann auch>Integer.MAX_VALUE große Pseudoarrays zuweisen, im Gegensatz zu Java-Arrays.

java.nio.ByteBuffer.allocateDirect(...) tatsächlich nutzt die gleiche Unsafe Klasse hinter den Kulissen Byte-Puffer zuzuordnen, und man konnte ByteBuffer.allocateDirect(8*size).asDoubleBuffer() verwenden, um es ein DoubleBuffer anzupassen, aber ByteBuffer.allocateDirect(...) noch den Puffer mit Nullen initialisiert, so könnte es eine Performance-Overhead hat.

0

Als ich die Frage zu verstehen, ist das eigentliche Problem mit einem riesigen zwei dimensionalen Array Zuweisung, wie sie in den Kommentaren erwähnt

„static int [] [] Lookup = new int [113088217] [2] ; funktioniert nicht, während private final static int [] [] lookup = neuer int [11308821] [2]; (10 mal weniger) in weniger als einer Sekunde geht "

Angenommen, das ist richtig, ja, für riesig Zahlen ist es Knochen langsam. Sie sind nicht Zuweisen eines einzelnen Blocks von 113088217 * 2 Ints! Sie ordnen 113088217 individuelle Arrays, die Objekte sind, von denen jede auf dem Heap zugeordnet werden muss: das bedeutet, dass die JVM mehr als 100 Millionen Mal Speicherplatz sucht, es als verwendet markiert, möglicherweise GC beim Speicher ausgeführt wird tight, etc ... Die Arrays nehmen jeweils erhebliche (in diesen riesigen Zahlen) zusätzlichen Speicher, plus jeder von ihnen enthält 2 Ints.

Für diesen großen Fall:

1) die Indizes wechseln und

static int[][] lookup = new int[2][113088217]

Statt Zuteilung 113 Millionen Arrays gehen, ordnet diese zwei. Wenn Sie die Suchvorgänge im Array durchführen, wechseln Sie die beiden Indizes. Machen

2) ein 1D-Array und tun das Lookup selbst

static int[] lookup = new int[2*113088217];

Dies erfordert einfache mathematische tat den richtigen Index zu finden. Anstatt direkt auf das Array zuzugreifen, schreiben Sie eine Funktion, um die Mathematik zu machen, und der aufrufende Code sollte das verwenden.

Verwandte Themen