Bir dizi neden Yinelenebilir'e atanamıyor?


186

Java5 ile şunları yazabiliriz:

Foo[] foos = ...
for (Foo foo : foos) 

veya sadece for döngüsünde bir Yinelenebilir kullanarak. Bu çok kullanışlı.

Ancak bu şekilde yinelenebilir için genel bir yöntem yazamazsınız:

public void bar(Iterable<Foo> foos) { .. }

ve yinelenebilir olmadığı için onu bir dizi ile çağırmak:

Foo[] foos = { .. };
bar(foos);  // compile time error 

Bu tasarım kararının arkasındaki nedenleri merak ediyorum.


8
Arrays.asList yeterince iyi sanırım
dfa

17
bu felsefi bir sorudur
dfa

2
Java 5 + 'da dizilerle başa çıkmak için iyi bir neden varargs yöntemleridir.
Jeff Walker

2
@Torsten: true, ancak bir Tekrarlanabilir kabul eden bir yönteme geçiyorsanız, büyük olasılıkla herhangi bir değişiklik yapmazsınız.
Michael Myers

5
Aslında Arrays.asList olduğu değil o ilkel türleri dizileri çalışmaz çünkü yeterince iyi. İlkel türlerin öğelerini genel olarak yinelemenin (kutulu) tek yerleşik yolu yansıma, kullanmaktır java.lang.reflect.Array, ancak performansı zayıftır. Ancak, isterseniz ilkel tür dizilerini sarmak için kendi yineleyicilerinizi (veya Liste uygulamalarınızı!) Yazabilirsiniz.
Boann

Yanıtlar:


78

Diziler arayüzleri ( Cloneableve java.io.Serializable) uygulayabilir . Öyleyse neden olmasın Iterable? Sanırım Iterablebir iteratoryöntem ekleyen kuvvetler ve diziler yöntem uygulamaz. char[]geçersiz kılmaz toString. Her neyse, referans dizileri ideal kullanımlardan daha az düşünülmelidir List. Dfa yorumları olarak, Arrays.asListdönüşümü sizin için açıkça yapacaktır.

(Bunu söyledikten clonesonra dizileri çağırabilirsiniz .)


23
> "... ve diziler yöntem uygulamaz." Bunun başka bir felsefi soru olduğunu düşünüyorum; diziler hiç ilkel türler değildi ve Java felsefesi "Her şey bir nesnedir (ilkel türler hariç). Öyleyse neden dizinin başından beri bir dizi kullanmak isteyeceği bir gazilyon işlemi olsa bile yöntemler uygulamadı. Oh, bu doğru, diziler jenerikler üzgün bir gezi olarak gelmeden önce güçlü bir şekilde yazılmış koleksiyonlardı.
fatuhoku

2
Bir dizide veri varsa, bayt [] 'ın akışlardan okunması gibi düşük düzeyli, performans açısından kritik işler yapmanız mümkündür. Dizileri yineleyememe, muhtemelen @Gareth'in söylediği gibi, Java jeneriklerinden temel argümanları tür argümanları olarak desteklemiyor olabilir.
Drew Noakes

2
@FatuHoku Jenerik önermek üzücü bir gezi yanlıştı. Jeneriklerin arzu edilebilirliği her zaman takdir edildi. Diziler ilkel değildir (öyle olduklarını söylemedim), ancak düşük seviyelidirler. Dizilerle yapmak istediğiniz tek şey, bunları vektör benzeri yapılar için bir uygulama detayı olarak kullanmaktır.
Tom Hawtin - tackline

1
Iterator<T>Ayrıca remove(T)atmak için izin verilir, ancak gerektirir UnsupportedOperationException.
wchargin

Diziler yöntemleri uygularlar: tüm yöntemleri uygularlar java.lang.Object.
mhsmith

59

Dizi bir Nesnedir, ancak öğeleri olmayabilir. Dizi, yinelenebilir ile başa çıkamayan int gibi ilkel bir tür içerebilir. En azından tahmin ettiğim şey bu.


3
Bu, Iterablearayüzü desteklemek için , sargı sınıflarını kullanmak için ilkel dizilerin uzmanlaşması gerektiği anlamına gelir . Yine de bunların hiçbiri gerçekten önemli değil, çünkü tür parametrelerinin hepsi zaten sahte.
thejoshwolfe

8
Bu, Nesne dizilerinin Yinelenebilir uygulamasını engellemez. Ayrıca, ilkel dizilerin sarılı tip için yinelenebilir uygulanmasını engellemez.
Boann

1
Otomatik boks bunu halledebilir
Tim Büthe

Bence bu doğru sebep. Jenerik ilkel türde (örneğin, destek gelene kadar, temel tür diziler için tatmin edici çalışmaz List<int>yerine List<Integer>, vb). Bir hack sarmalayıcılarla yapılabilir, ancak performans kaybı - ve daha da önemlisi - bu hack yapıldıysa, gelecekte Java'da düzgün bir şekilde uygulanmasını önleyecektir (örneğin int[].iterator(), sonsuza dek dönmek Iterator<Integer>yerine kilitlenecekti Iterator<int>). Belki de, Java için gelecek değer türleri + genel-uzmanlaşma (proje valhalla) dizilerin uygulanmasını sağlayacaktır Iterable.
Bjarke

16

Diziler Iterable, .NET dizilerinin konuma göre salt okunur rasgele erişime izin veren bir arabirimi desteklememesi için desteklemelidir , yalnızca desteklememelidir (standart olarak tanımlanan böyle bir arabirim yoktur). Temel olarak, çerçevelerin genellikle sinir bozucu küçük boşlukları vardır, bu da kimsenin düzeltmeye zaman ayırmaya değmez. Onları kendimizi en uygun şekilde düzeltebilmemiz önemli değil, ama çoğu zaman yapamayız.

GÜNCELLEME: Eşit olmak için, .NET dizilerinin konuma göre rastgele erişimi destekleyen bir arabirimi desteklemediğinden bahsetmiştim (ayrıca yorumuma bakın). Ancak .NET 4.5'te tam olarak bu arabirim tanımlanmıştır ve diziler ve List<T>sınıf tarafından desteklenir :

IReadOnlyList<int> a = new[] {1, 2, 3, 4};
IReadOnlyList<int> b = new List<int> { 1, 2, 3, 4 };

Değişken liste arayüzü IList<T>devralmadığı için hepsi hala mükemmel değil IReadOnlyList<T>:

IList<int> c = new List<int> { 1, 2, 3, 4 };
IReadOnlyList<int> d = c; // error

Belki de böyle bir değişiklikle geriye dönük uyumluluk olasılığı vardır.

Java'nın yeni sürümlerinde benzer şeyler konusunda ilerleme olursa, yorumlarda bilmek isterim! :)


8
.NET dizileri IList arabirimini uygular
Tom Gillen

2
@Aphid - Ben salt rastgele erişim dedim . IList<T>modifikasyon işlemlerini gösterir. Eğer çok iyi olurdu IList<T>bir miras gibi bir şey vardı IReadonlyList<T>sadece olurdu arayüzü Countve T this[int]ve kalıtsal IEnumerable<T>(zaten salt okunur numaralandırma destekler). Başka bir harika şey, Reverseuzantı yönteminin sorgulayabileceği ters sıralı bir numaralandırıcı elde etmek için bir arayüz olacaktır ( Countuzantı yönteminin ICollectionkendini optimize etmek için
sorguladığı gibi

Evet, işler bu şekilde tasarlanmış olsaydı daha iyi olurdu. IList arabirimi, diziye uygun şekilde uygulanan IsReadOnly ve IsFixedSize özelliklerini tanımlar. Bana verilen listenin aslında salt okunur olduğunu kontrol etmek için derleme zamanı sunmadığından, bunu yapmanın her zaman çok kötü bir yolu olarak beni vurdu ve çok nadiren bu özellikleri kontrol eden kodu görüyorum.
Tom Gillen

1
.NET Diziler uygulamak IListve ICollection.NET 1.1 beri ve IList<T>ve ICollection<T>.NET 2.0 beri. Bu, Java'nın rekabetin çok gerisinde olduğu başka bir durum.
Amir Abiri

@TomGillen: En büyük sorunum, IListdaha fazla sorgulanabilir özellik sağlamaması. Uygun bir kümenin yeni başlayanlar için IsUpdateable, IsResizable, IsReadOnly, IsFixedSize ve ExistingElementsAreImmutable içermesi gerektiğini söyleyebilirim. Bir başvurunun kodun, tipik olarak bir listeyi değiştirmeden değiştirip değiştiremeyeceği sorusu, değiştirmesi gerekmeyen bir listeye başvurusu olan kodun bu başvuruyu doğrudan dış kodla güvenli bir şekilde paylaşıp paylaşamayacağı veya listenin bazı yönlerinin asla değişmeyeceğini varsayabilir.
supercat

14

Ne yazık ki, diziler ' class-hüzük' değildir . IterableArayüzü uygulamıyorlar .

Diziler artık Clonable ve Serializable uygulayan nesneler olsa da, bir dizinin normal anlamda bir nesne olmadığına ve arabirimi uygulamadığına inanıyorum .

Bunları her döngü için kullanabilmenizin nedeni, Sun'ın diziler için bazı sözdizim şekeri eklemesidir (bu özel bir durumdur).

Diziler Java 1 ile 'neredeyse nesneler' olarak başladığından, Java'da gerçek nesneler yapmak için çok büyük bir değişiklik olurdu .


14
Yine de, her döngü için şeker var, neden tekrarlanabilir için şeker olamaz?
Michael Myers

8
@mmyers: Her biri için kullanılan şeker derleme zamanı şekeri. Bunu yapmak VM şekerinden çok daha kolay . Söyledikten sonra, .NET dizileri bu alanda önemli ölçüde daha iyi ...
Jon Skeet

12
Diziler olabilir arabirimlerini. Onlar uygular Cloneableve Serializablearayüzler.
notnoop

34
java dizileri her anlamda nesnedir. Lütfen bu yanlış bilgileri biraz kaldırın. Sadece tekrarlanabilir bir uygulama yapmıyorlar.
ykaganovich

5
Dizi bir Nesnedir. Faydalı desteği destekler : wait (), wait (n), wait (n, m), notify (), notifyAll (), finalize (), toString () 'in anlamsız bir uygulaması. GetClass () .
Peter Lawrey

1

Derleyici aslında for eachbir dizi üzerindeki forbir sayaç değişkeni ile basit bir döngü dönüştürür .

Aşağıdaki derleme

public void doArrayForEach() {
    int[] ints = new int[5];

    for(int i : ints) {
        System.out.println(i);
    }
}

ve sonra .class dosya verilerinin ayrıştırılması

public void doArrayForEach() {
    int[] ints = new int[5];
    int[] var2 = ints;
    int var3 = ints.length;

    for(int var4 = 0; var4 < var3; ++var4) {
        int i = var2[var4];
        System.out.println(i);
    }
}
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.