wake-up-neo.com

Unveränderliches Array in Java

Gibt es eine unveränderliche Alternative zu den primitiven Arrays in Java? Das Erstellen eines primitiven Arrays final hindert einen nicht daran, so etwas zu tun

final int[] array = new int[] {0, 1, 2, 3};
array[0] = 42;

Ich möchte, dass die Elemente des Arrays nicht veränderbar sind.

135
ignoramus

Nicht bei primitiven Arrays. Sie müssen eine Liste oder eine andere Datenstruktur verwenden:

List<Integer> items = Collections.unmodifiableList(Arrays.asList(0,1,2,3));
148
Jason S

Meine Empfehlung ist, kein Array oder eine unmodifiableList zu verwenden, sondern die für diesen Zweck vorhandene Guava 's ImmutableList zu verwenden.

ImmutableList<Integer> values = ImmutableList.of(0, 1, 2, 3);
68
ColinD

Wie bereits erwähnt, können Sie in Java keine unveränderlichen Arrays verwenden.

Wenn Sie unbedingt eine Methode benötigen, die ein Array zurückgibt, das das ursprüngliche Array nicht beeinflusst, müssen Sie das Array jedes Mal klonen:

public int[] getFooArray() {
  return fooArray == null ? null : fooArray.clone();
}

Offensichtlich ist dies ziemlich teuer (da Sie bei jedem Aufruf des Getters eine vollständige Kopie erstellen), aber wenn Sie die Schnittstelle nicht ändern können (um beispielsweise eine List zu verwenden), und der Client nicht Ihre Interna ändern kann, dann kann es notwendig sein.

Diese Technik wird als defensive Kopie bezeichnet.

20
Joachim Sauer

Es gibt eine Möglichkeit, ein unveränderliches Array in Java zu erstellen:

final String[] IMMUTABLE = new String[0];

Arrays mit 0 Elementen können (offensichtlich) nicht mutiert werden.

Dies kann sich als praktisch erweisen, wenn Sie die List.toArray-Methode verwenden, um eine List in ein Array zu konvertieren. Da selbst ein leeres Array etwas Speicherplatz beansprucht, können Sie diese Speicherzuordnung speichern, indem Sie ein konstantes leeres Array erstellen und es immer an die toArray-Methode übergeben. Diese Methode weist ein neues Array zu, wenn das übergebene Array nicht über genügend Speicherplatz verfügt. Wenn dies jedoch der Fall ist (die Liste ist leer), wird das von Ihnen übergebene Array zurückgegeben, sodass Sie dieses Array bei jedem Aufruf von toArray wiederverwenden können eine leere List.

final static String[] EMPTY_STRING_ARRAY = new String[0];

List<String> emptyList = new ArrayList<String>();
return emptyList.toArray(EMPTY_STRING_ARRAY); // returns EMPTY_STRING_ARRAY
14
Brigham

Noch eine Antwort

static class ImmutableArray<T> {
    private final T[] array;

    private ImmutableArray(T[] a){
        array = Arrays.copyOf(a, a.length);
    }

    public static <T> ImmutableArray<T> from(T[] a){
        return new ImmutableArray<T>(a);
    }

    public T get(int index){
        return array[index];
    }
}

{
    final ImmutableArray<String> sample = ImmutableArray.from(new String[]{"a", "b", "c"});
}
5
aeracode

Wenn Sie (aus Leistungsgründen oder um Speicherplatz zu sparen) natives 'int' anstelle von 'Java.lang.Integer' benötigen, müssen Sie wahrscheinlich eine eigene Wrapper-Klasse schreiben. Es gibt verschiedene IntArray-Implementierungen im Netz, aber keine (ich fand) war unveränderlich: Koders IntArray , Lucene IntArray . Es gibt wahrscheinlich noch andere.

3
Thomas Mueller

Ab Java 9 können Sie List.of(...), JavaDoc verwenden.

Diese Methode gibt eine unveränderliche List zurück und ist sehr effizient.

3
rmuller

Seit Guava 22 können Sie ab Paket com.google.common.primitives drei neue Klassen verwenden, die im Vergleich zu ImmutableList einen geringeren Speicherbedarf haben.

Sie haben auch einen Erbauer. Beispiel:

int size = 2;
ImmutableLongArray longArray = ImmutableLongArray.builder(size)
  .add(1L)
  .add(2L)
  .build();

oder, wenn die Größe zur Kompilierzeit bekannt ist:

ImmutableLongArray longArray = ImmutableLongArray.of(1L, 2L);

Dies ist eine weitere Möglichkeit, eine unveränderliche Ansicht eines Arrays für Java-Grundelemente zu erhalten.

2
JeanValjean

Nein das ist nicht möglich. Man könnte jedoch so etwas tun:

List<Integer> temp = new ArrayList<Integer>();
temp.add(Integer.valueOf(0));
temp.add(Integer.valueOf(2));
temp.add(Integer.valueOf(3));
temp.add(Integer.valueOf(4));
List<Integer> immutable = Collections.unmodifiableList(temp);

Dies erfordert die Verwendung von Wrappern und ist eine Liste, kein Array, aber die nächste, die Sie erhalten werden.

1
user439793

In einigen Situationen ist es leichter, diese statische Methode aus der Google Guava-Bibliothek zu verwenden: List<Integer> Ints.asList(int... backingArray)

Beispiele:

  • List<Integer> x1 = Ints.asList(0, 1, 2, 3)
  • List<Integer> x1 = Ints.asList(new int[] { 0, 1, 2, 3})
1
kevinarpe

Wenn Sie sowohl Mutabilität als auch Boxen vermeiden möchten, gibt es keinen Ausweg aus der Box. Sie können jedoch eine Klasse erstellen, die ein primitives Array enthält und den schreibgeschützten Zugriff auf Elemente über Methoden ermöglicht.

1
Sarge Borsch

Die von (E ... elements) - Methode in Java9 kann verwendet werden, um eine unveränderliche Liste mit nur einer Zeile zu erstellen:

List<Integer> items = List.of(1,2,3,4,5);

Die obige Methode gibt eine unveränderliche Liste zurück, die eine beliebige Anzahl von Elementen enthält. Das Hinzufügen einer ganzen Zahl zu dieser Liste würde Java.lang.UnsupportedOperationExceptionexception zur Folge haben. Diese Methode akzeptiert auch ein einzelnes Array als Argument.

String[] array = ... ;
List<String[]> list = List.<String[]>of(array);
0
i_am_zero

Obwohl es stimmt, dass Collections.unmodifiableList() funktioniert, kann es vorkommen, dass Sie über eine große Bibliothek verfügen, für die bereits Methoden definiert sind, um Arrays zurückzugeben (z. B. String[]). Um zu verhindern, dass sie beschädigt werden, können Sie Hilfsarrays definieren, die die Werte speichern:

public class Test {
    private final String[] original;
    private final String[] auxiliary;
    /** constructor */
    public Test(String[] _values) {
        original = new String[_values.length];
        // Pre-allocated array.
        auxiliary = new String[_values.length];
        System.arraycopy(_values, 0, original, 0, _values.length);
    }
    /** Get array values. */
    public String[] getValues() {
        // No need to call clone() - we pre-allocated auxiliary.
        System.arraycopy(original, 0, auxiliary, 0, original.length);
        return auxiliary;
    }
}

Zu testen:

    Test test = new Test(new String[]{"a", "b", "C"});
    System.out.println(Arrays.asList(test.getValues()));
    String[] values = test.getValues();
    values[0] = "foobar";
    // At this point, "foobar" exist in "auxiliary" but since we are 
    // copying "original" to "auxiliary" for each call, the next line
    // will print the original values "a", "b", "c".
    System.out.println(Arrays.asList(test.getValues()));

Nicht perfekt, aber zumindest haben Sie "pseudo-unveränderbare Arrays" (aus Sicht der Klasse), und dies wird den zugehörigen Code nicht beschädigen.

0
miguelt