16 Ağustos 2011 Salı

Bazı Önemli Sınıflar-java.util.Arrays

Dizilere uygulanabilecek işlemlerin eleman erişim ve güncelleme ile uzunluk sorgulamaya sınırlı olması, size Java'da bir şeylerin eksik olduğunu düşündürebilir. Java platformu tasarımcıları da bu görüşü paylaşıyor olmalılar ki, java.util paketindeki Arrays sınıfı vasıtasıyla epey geniş bir işlevsellik sunmuşlar. İşte bu yazıda yapacağımız, anılan sınıf tarafından sağlanan ve değişik eleman türlerine göre ezilmiş metotlara bakmak olacak.

Kopyalama işlemi ile başlayalım. Dizileri tanıttığımız yazıdan🔎 da hatırlayacağınız gibi, dizi türlü bir değişkenin bir diğerine atanması, etkisini tutacaklar aracılığıyla göstereceği için kopyalama değil paylaşmaya neden olacaktır; kopyalama döngü içinde her elemanın teker teker kopyalanması ile mümkündür. Ancak, standart olmayan bu çözümün yerine, ilk argümanındaki dizinin başlangıcından itibaren ikinci argümandaki kadar elemanı kopyalayan ve çıkardığı kopyanın tutacağını döndüren Arrays.copyOf metodunun kullanılması yerinde olacaktır.
import java.util.Arrays;
...
Integer[] tekler = new Integer[] {1, 3, 5, 7, 9};
Integer[] tKopya = Arrays.copyOf(tekler, tekler.length);
Benzer bir işleve sahip Arrays.copyOfRange, ikinci ve üçüncü argümanlarının işaret ettiği indisler arasındaki dilimi kopyalayarak işini görür. Dolayısıyla, Arrays.copyOf ile Arrays.copyOfRange arasında şu denklik kurulabilir.

Arrays.copyOf(dz. i) ≡ Arrays.copyOf(dz, 0, i)

Bileşke türlü değerlerin Java'da tutacak ve nesne olmak üzere iki parça ile temsil edildiğini unutanları şaşırtacak bir diğer işlem eşitlik denetimidir. Ancak, == ile işin olmayacağını bilip şansını equals iletisi ile denemek isteyenler için de sonuç hayal kırıklığı olacaktır. Bu durumun nedeni dizi türlerinin Java programlama dili tarafından ele alınışında yatar: eleman sayısını dizinin türünü belirlemekte kullanmayan Java derleyicisi, her farklı eleman türü için, programcı tarafından doğrudan erişilemeyen ve Object sınıfından kalıtlayan özel bir sınıf sentezler. Bu bilgi, sentezlenen sınıflarda Object'teki equals gerçekleştiriminin ezilmediği bilgisiyle birleştirildiğinde, equals kullanımının neden derdimize çare olmadığı görülecektir: dizi nesnelerine equals iletisinin gönderilmesi Object'te sağlanan ve == ile aynı şekilde çalışan metodun işlemesine neden olacaktır. Bir diğer deyişle, dizi nesneleri için equals ile == arasında bir fark yoktur. O zaman, ne yapmamız gerek? Yanıt, Arrays sınıfındaki iki metottan birini kullanmaktan geçer. Bunlardan ilki, tek boyutlu dizilerin eşitlik denetimini yapan Arrays.equals metodudur.
boolean eşitMi = tekler == tKopya; // eşitMi ← false
eşitMi = tekler.equals(tKopya); // eşitMi ← false
eşitMi = Arrays.equals(tekler, tKopya); // eşitMi ← true
Arrays.equals metodunun neden tek boyutlu dizilere sınırlı olduğunu, çok boyutlu dizilerin Java'da nasıl temsil edildiğine bir kez daha değinerek açıklık getirelim: Java'da çok boyutlu diziler, elemanları dizi olan diziler şeklinde tutulur. Dolayısıyla, dizinin elemanlarına equals iletisini göndermek suretiyle işini gören Arrays.equals metodunun çok boyutlu diziler için iş görmesi olanaksızdır. Bunu, aşağıdaki kod parçası üzerinden görelim.
char[][] bulmaca = new char[10][12];
char[][] bulmaca2 = new char[10][12];
// Dizileri eşit içeriğe sahip olacak şekilde doldur
eşitMi = Arrays.equals(bulmaca, bulmaca2); // eşitMi ← false
eşitMi = Arrays.deepEquals(bulmaca, bulmaca2); // eşitMi ← true
Yukarıdaki dizilerin eşitlik denetimi için Arrays.equals metodunun kullanılması, dizilerin eleman türü olan ve derleyici tarafından sentezlenen char[] sınıfındaki equals metodunun kullanılması ile işini görecektir. Bu ise, söz konusu metodun == ile aynı şekilde çalışması nedeniyle, dizilerin karşılıklı elemanlarının eşitlik yerine aynılık denetimi yapılarak kontrol edilmesi anlamına gelir ve beklediğimiz sonucu vermez. Çözüm, Arrays.deepEquals metodunun kullanımından geçer.

Eşitlik denetimi için öne sürülen sebepten ötürü, hoş yazım ve kıyım işlemleri1 de metot çiftleri ile karşılanır: Arrays.toString, Arrays.deepToString ve Arrays.hashCode, Arrays.deepHashCode.

Arrays sınıfında yer alan bir diğer metot, kendisine geçirilen dizinin elemanlarını aynı değer ile doldurmaya yarayan fill metodudur. Bu metot, dizinin tümü ve bir dilimi üzerinde etkisini gösteren iki farklı uyarlamaya sahiptir ve bu uyarlamalar aşağıdaki denklikle ilişkilendirilebilir.

Arrays.fill(dz. i) ≡ Arrays.fill(dz, 0, dz.length, i)

Değineceğimiz bir sonraki metot, belki de gezegenimizdeki kurulu bilgisayar sistemlerinin işlemcilerini en çok meşgul eden arama işleminin gerçekleştirimini sağlar. Algoritma derslerinden tanıdık ikili aramayı gerçekleştiren Arrays.binarySearch metodu, ilk argümanında sağlanan elemanları artan sırada dizilmiş dizinin içinde bir anahtar değerin geçip geçmediğine bakar. Denetim sonucunun olumlu olması durumunda, anahtarın geçtiği dizi elemanının indisi döndürülür;2 aramanın başarısız olması durumunda ise, anahtarın sırayı bozmayacak şekilde diziye eklenmesi halinde eklemenin yapılacağı indisin -1 ile çarpımından 1 çıkarılması sonucu elde edilen değer döndürülür. Daha hızlı sonuç elde edilmesi amacıyla, arama dizinin belirli bir dilimine sınırlandırılabilir. Tüm dizi ve dizi dilimi üzerinde çalışan uyarlamalar arasındaki ilişki aşağıda verilmiştir.

Arrays.binarySearch(dz. anahtar)

Arrays.binarySearch(dz, 0, dz.length, anahtar)


Arrays.binarySearch metodunun bileşke türlü elemanlara sahip bir dizide arama yapması öncesinde, ilişkin türe dair sınıfın gerçekleştiriminde equals metodunun uygun bir biçimde sağlanmış olmasına dikkat edilmelidir. Aksi takdirde, dizide var olan bir anahtarın bulunmadığının ilan edilmesi gibi bir hata ortaya çıkabilir. Böylesine bir durumun önüne geçmek için başvurulacak ikinci bir yol, java.util.Comparator arayüzünü gerçekleştiren karşılaştırıcı bir sınıfta equals iletisinin gerçekleştirilmesi ve bu sınıfın bir nesnesinin diğer argümanların ardından son argüman olarak Arrays.binarySearch'e geçirilmesidir.

İkili aramanın hünerini sıralı bir dizi üzerinde göstermesi, dizinin sıralı bir şekilde oluşturulması veya ikili arama öncesinde sıralanması zorunluluğunu da beraberinde getirir. Programcıya yüklenen bu sorumluluk, Arrays.sort metodu ile hafifletilebilir. İsteğe göre dizinin tümü veya belirli bir dilimini sıralayan bu metot, dizinin eleman türünün karşılaştırılabilir olmasını bekler. Bu ise, bileşke türler için ilişkin sınıfın Comparable arayüzünü gerçekleştirmesi ve/veya karşılaştırma ölçütünün java.util.Comparator arayüzünü gerçekleştiren bir sınıf vasıtasıyla söz konusu işlevselliğin sağlanması gerektiği anlamına gelir.

Arrays.sort(dz) ≡ Arrays.sort(dz, 0, dz.length)

Göz atacağımız son metot olan Arrays.asList, yegâne argümanındaki diziyi Veri Kapları Çerçevesi'nce tanımlanan java.util.List ve java.util.RandomAccess arayüzlerine sahip bir nesneye çevirir ve bu nesneyi gösteren bir java.util.List tutacağı döndürür. Nesneye gönderilen iletilerin diziyi de etkilemesi nedeniyle, bu işlem dizimizin işlevselliğinin java.util.List arayüzündeki iletilerle—java.util.RandomAccess bir gösterge arayüz (İng., marker interface)3 olduğu için bu yönde bir katkı sağlamaz—genişletilmesini sağlar. Ancak, bu arayüzün veri kümesini büyütmeye yönelik iletilerinin, dizinin statik doğası nedeniyle, kullanılamayacağı unutulmamalıdır.
List<Integer> tList = Arrays.asList(tekler);
// tekler[2]'de 121 değerine sahip olacak.
tList.set(2, 121);
tList.add(123); // → UnsupportedOperationException


  1. Kıyım işlemi, doğrudan veya dolaylı bir biçimde dizi gibi rasgele erişimli veri yapılarının kullanıldığı arama algoritmalarında, aranan anahtar değerinin tamsayıya çevrilmesini sağlayarak rasgele erişimli veri yapılarının avantajlarından yararlanmayı olanaklı kılar.
  2. Aranmakta olan anahtar değerin dizi içinde birden çok geçmesi durumunda, hangi indisin döndürüleceği konusunda garantili bir tahmin yapılması olanaklı değildir.
  3. İleti içermeyen arayüzlere gösterge arayüz denir.