Java'da değişmez dizi


158

Java'daki ilkel dizilere değişmez bir alternatif var mı? İlkel bir dizi yapmak finalaslında birinin böyle bir şey yapmasını engellemez

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

Dizi öğelerinin değişmez olmasını istiyorum.


43
Kendinize bir iyilik yapın ve kendi Liste / Koleksiyonunuzu ( nadiren ) uygulamanız gerekiyorsa, Java'daki dizileri 1) io 2) ağır sayı crunching 3) dışında herhangi bir şey için kullanmayı bırakın . Bu soru ile yeni keşfettiğiniz gibi, son derece esnek ve eskimişlerdir.
whaley

39
@Whaley, performanslar ve "dinamik" dizilere ihtiyacınız olmayan kodlar. Diziler birçok yerde hala yararlıdır, o kadar nadir değildir.
Colin Hebert

10
@Colin: evet ama ciddi şekilde kısıtlıyorlar; "Burada gerçekten bir diziye ihtiyacım var mı yoksa bunun yerine bir Liste kullanabilir miyim?"
Jason S

7
@Colin: Yalnızca ihtiyacınız olduğunda optimize edin. Kendinizi yarım dakika harcadığınızda, örneğin bir diziyi genişleten veya bir dizini for-loop kapsamı dışında tutan bir şey eklediğinizde, patronunuzun zamanını zaten boşa harcadınız. İlk önce derleyin, gerektiğinde ve nerede optimize edin - ve çoğu uygulamada, Listelerin yerine dizileri koymaz.
fwielstra

9
Peki, neden kimse bahsetmedi int[]ve new int[]ÇOK yazmak daha kolay List<Integer>ve new ArrayList<Integer>? XD
lcn

Yanıtlar:


164

İlkel dizilerle değil. Bir Liste veya başka bir veri yapısı kullanmanız gerekir:

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

18
Bir şekilde Arrays.asList'i (T ...) hiç bilmiyordum. Sanırım şimdi ListUtils.list (T ...) 'den kurtulabilirim.
MattRS

3
Bu arada, Arrays.asList değiştirilemez bir liste veriyor
mauhiz

3
@tony ArrayList java.util's değil
mauhiz

11
@mauhiz Arrays.asListolduğu değil unmodifiable. docs.oracle.com/javase/7/docs/api/java/util/… "Belirtilen dizi tarafından desteklenen sabit boyutlu bir liste döndürür. (Döndürülen listede yapılan değişiklikler diziye" üzerinden yazma ").
Jason S

7
@JasonS iyi, Arrays.asList()gerçekten görünüyor anlamda unmodifiable can not addveya removeiade kalemleri java.util.Arrays.ArrayList( karıştırılmamalıdır ile java.util.ArrayList) - bu işlemler sadece uygulanmadı. Belki @mauhiz bunu söylemeye çalışır? Ama elbette mevcut elemanları değiştirebilirsin List<> aslist = Arrays.asList(...); aslist.set(index, element), bu yüzden java.util.Arrays.ArrayListkesinlikle hareketsiz değil , QED Sadece sonucu asListile normal arasındaki farkı vurgulamak için yorum eklediArrayList
NIA

73

Benim öneri bir dizi veya değil kullanmaktır unmodifiableListama kullanmak Guava 'nın ImmutableList bu amaç için vardır.

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

3
+1 Guava'nın ImmutableList, Collections.unmodifiableList'ten bile daha iyidir, çünkü ayrı bir türdür.
sleske

29
ImmutableList genellikle daha iyidir (kullanım durumuna bağlıdır) çünkü değişmez. Collections.unmodifiableList değiştirilemez. Aksine, alıcıların değiştiremeyeceği bir görünümdür, ancak orijinal kaynak değişebilir.
Charlie Collins

2
@CharlieCollins, orijinal kaynağa erişmenin bir yolu yoksa, Collections.unmodifiableListbir Listeyi değiştirilemez hale getirmek için yeterli mi?
savanibharat

1
@savanibharat Evet.
Sadece bir öğrenci

20

Diğerlerinin de belirttiği gibi, Java'da değişmez dizilere sahip olamazsınız.

Orijinal diziyi etkilemeyen bir dizi döndüren bir yönteme kesinlikle ihtiyacınız varsa, diziyi her seferinde klonlamanız gerekir:

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

Açıkçası bu oldukça pahalıdır (alıcıyı her aradığınızda tam bir kopya oluşturacağınız için), ancak arayüzü değiştiremiyorsanız ( Listörneğin kullanmak için) ve istemcinin dahili bilgilerinizi değiştirmesini riske atamazsanız, gerekli olabilir.

Bu tekniğe defansif bir kopya yapmak denir.


3
Bir alıcıya ihtiyacı olduğunu nereden söyledi?
Erick Robertson

6
@Erik: yapmadı, ama bu değişmez veri yapıları için çok yaygın bir kullanım örneğidir (cevabı genel olarak yöntemlere atıfta bulunmak için değiştirdim, çünkü çözüm her yerde uygulandığı için, alıcılarda daha yaygın olsa bile).
Joachim Sauer

7
Daha iyi kullanmak mı clone()yoksa Arrays.copy()burada?
kevinarpe

Hatırladığım kadarıyla Joshua Bloch'un "Etkili Java" sına göre, klon () kesinlikle tercih edilen yoldur.
Vincent

1
13. Maddenin son cümlesi, "Klonu mantıklı bir şekilde geçersiz kıl": "Kural olarak, kopyalama işlevselliği en iyi yapıcılar veya fabrikalar tarafından sağlanır. Bu kurala dikkat çeken bir istisna, en iyi klon yöntemiyle kopyalanan dizilerdir."
Vincent

14

Java'da değişmez bir dizi yapmanın bir yolu vardır:

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

0 elemanlı diziler (açıkça) mutasyona uğratılamaz.

Bir diziyi List.toArraydönüştürmek için yöntem kullanıyorsanız bu aslında kullanışlı olabilir List. Boş bir dizi bile bellek aldığından, sabit bir boş dizi oluşturarak ve bunu her zaman toArrayyönteme ileterek bellek ayırmayı kaydedebilirsiniz . Eğer geçmek Dizi yeterli alan yoksa bu yöntem yeni bir dizi tahsis edecek, ancak (liste boşsa) yaparsa, onunla bu dizide aramak her zaman yeniden izin, geçtin dizi döndürür toArraybir de boş 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

3
Ancak bu OP'nin içindeki verilerle değişmez bir dizi elde etmesine yardımcı olmaz.
Sridhar Sarnobat

1
@ Sridhar-Sarnobat Bu sorunun cevabı. Java'da içindeki verilerle değişmez bir dizi oluşturmanın bir yolu yoktur.
Brigham

1
Bu basit sözdizimini daha çok seviyorum: private static final String [] EMPTY_STRING_ARRAY = {}; (Açıkçası "mini-Markdown" ın nasıl yapılacağını anlayamadım. Önizleme düğmesi iyi olurdu.)
Wes

6

Başka bir cevap

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"});
}

Herhangi bir ayarlayıcı yöntemi sağlamadığımız için bu diziyi yapıcıda kopyalamanın ne yararı var?
vijaya kumar

Giriş dizisinin içeriği daha sonra değiştirilebilir. Orijinal durumunu korumak istiyoruz, bu yüzden kopyalıyoruz.
aeracode

4

Java 9 itibariyle kullanabileceğiniz List.of(...), javadoc .

Bu yöntem değişmez bir sonuç verir Listve çok etkilidir.


3

'Java.lang.Integer' yerine (performans nedeniyle veya belleği kaydetmek için) yerel 'int' gerekiyorsa, muhtemelen kendi sarmalayıcı sınıfınızı yazmanız gerekir. İnternette çeşitli IntArray uygulamaları var, ancak hiçbiri (buldum) değişmezdi: Koders IntArray , Lucene IntArray . Muhtemelen başkaları da vardır.


3

Guava 22'den bu yana, paketten com.google.common.primitivesdaha düşük bellek alanına sahip üç yeni sınıf kullanabilirsiniz ImmutableList.

Ayrıca bir inşaatçı var. Misal:

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

veya boyut derleme zamanında biliniyorsa:

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

Bu, Java ilkelleri için bir dizinin değişmez bir görünümünü elde etmenin başka bir yoludur.


1

Hayır, bu mümkün değil. Ancak, böyle bir şey yapılabilir:

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);

Bu, sarmalayıcıların kullanılmasını gerektirir ve bir dizi değil, bir Liste'dir, ancak alacağınız en yakın şeydir.


4
Bunların hepsini yazmaya gerek yok valueOf(), otomatik boks bununla ilgilenecek. Ayrıca Arrays.asList(0, 2, 3, 4)çok daha özlü olurdu.
Joachim Sauer

@ Joachim: Kullanım noktası, valueOf()bellek tüketimini / geri dönüşümü azaltmak için dahili Integer nesne önbelleğini kullanmaktır.
Esko

4
@Esko: otomatik boks özelliğini okuyun. Aynı şeyi yapıyor, bu yüzden burada bir fark yok.
Joachim Sauer

1
Bir dönüştürme bir NPE olsun asla @John intbir etmek Integerolsa; sadece diğer tarafa dikkat etmelisin.
ColinD

1
@John: haklısın, tehlikeli olabilir. Ancak bundan tamamen kaçınmak yerine, tehlikeyi anlamak ve bundan kaçınmak daha iyidir.
Joachim Sauer

1

Bazı durumlarda, Google Guava kitaplığındaki bu statik yöntemi kullanmak daha hafif olacaktır: List<Integer> Ints.asList(int... backingArray)

Örnekler:

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

1

Hem değiştirilebilirlikten hem de bokstan kaçınmak istiyorsanız, kutudan çıkmanın bir yolu yoktur. Ancak, ilkel diziyi içinde tutan ve yöntem (ler) yoluyla öğelere salt okunur erişim sağlayan bir sınıf oluşturabilirsiniz.


1

(E ... öğeleri) yöntemi Java9 sadece satırını kullanarak değişmez bir liste oluşturmak için kullanılabilir:

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

Yukarıdaki yöntem, rastgele sayıda öğe içeren değişmez bir liste döndürür. Ve bu listeye herhangi bir tam sayı eklemek java.lang.UnsupportedOperationExceptionistisnayla sonuçlanır . Bu yöntem aynı zamanda tek bir diziyi bağımsız değişken olarak kabul eder.

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

0

Çalıştığı doğru olsa da Collections.unmodifiableList(), bazen dizileri döndürmek için zaten tanımlanmış yöntemlere sahip büyük bir kütüphaneniz olabilir (örn. String[]). Bunları kırmayı önlemek için, değerleri depolayacak yardımcı diziler tanımlayabilirsiniz:

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;
    }
}

Test etmek için:

    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()));

Mükemmel değil, ama en azından "sözde değişmez diziler" var (sınıf açısından) ve bu ilgili kodu bozmaz.


-3

Diziler, varyant parametreleri olarak sabitler (eğer olduysa) olarak geçmek için kullanışlıdır.


"Değişkenler parametresi" nedir? Hiç duymadım.
sleske

2
Sabitleri olarak dizileri kullanma olduğunu tam olarak birçok kişi FAIL nerede. Referans sabittir, ancak dizinin içeriği değiştirilebilir. Bir arayan / müşteri "sabit" inizin içeriğini değiştirebilir. Bu muhtemelen izin vermek istediğiniz bir şey değildir. Bir ImmutableList kullanın.
Charlie Collins

@CharlieCollins bu bile yansıma ile hacklenebilir. Java değişebilirlik açısından güvensiz bir dildir ... ve bu değişmeyecektir.
Görünen Ad

2
@SargeBorsch Bu doğru, ancak yansıma tabanlı korsanlık yapan herkes, API yayıncısının erişmesini istemeyebilecekleri şeyleri değiştirerek ateşle oynadıklarını bilmelidir. Bununla birlikte, bir API bir döndürürse int[], arayan, API'nin iç kısımlarını etkilemeden bu dizi ile istediklerini yapabileceklerini varsayabilir.
daiscog
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.