İçeriğe geç

Koleksiyonların İçi — ArrayList, LinkedList ve Iterator

Orta 8 dk Çok sık karşılaşılır

Önce şunu oku: HashMap, equals ve hashCode

30 saniyede özet

ArrayList numaralı koltuklara benzer: istediğine hemen gidersin ama başa birini sokmak herkesi kaydırır. LinkedList el ele tutuşan bir zincirdir: araya girmek kolay, birini bulmak uzun yürüyüş.

Aynı adı taşıyan, aynı düğmelere sahip iki alet düşün. Aynı düğmeye basıyorsun: biri hemen işini bitiriyor, öteki uzun uzun uğraşıyor.

Başa bir kişi eklemek: bir yerde herkes kalkar, öbüründe bir el tutulur.
Adım adım oku
  1. ArrayList'te elemanlar numaralı koltuklarda oturur. Başa birini oturtmak için 0 numaralı koltuğun boşalması gerekir.
  2. Bunun için arkadaki herkes, en sondan başlayarak birer koltuk kayar.
  3. LinkedList'te elemanlar el ele tutuşan düğümlerdir. Yeni düğüm yalnızca eski ilk düğümün elini tutar.
  4. Aynı iş, çok farklı zahmet: bir tarafta dört kaydırma, öbür tarafta tek bir yeni bağ.
  1. Bayt: İki listem var, ikisinin de tipi List. Başa bir eleman ekledim: biri anında bitti, öteki oyalandı.

  2. Sen: Aynı metot, farklı hız mı?

  3. Bayt: İçleri farklı. Biri numaralı koltuklar, öteki el ele tutuşan bir zincir.

  4. Bayt: Hangi işi sık yaptığını bil, listeyi ona göre seç. Çoğu zaman cevap ArrayList!

Java’da ikisinin de tipi List, ikisi de aynı metotlara sahip. Ama aynı satır birinde tek adım, ötekinde yüzlerce adım sürer.

ArrayList: bir dizi ve bir sayaç

ArrayList’in içinde sıra sıra dizilmiş koltuklar (bir dizi) ve kaç koltuğun dolu olduğunu tutan bir sayaç var. İndeks doğrudan koltuk numarasıdır: get(5000) kimseyi aramaz, doğrudan 5000 numaraya gider.

Koltuklar dolunca yaklaşık 1,5 kat büyük yeni bir salon açılır ve herkes oraya taşınır. Taşınma pahalıdır ama seyrek olur; bu yüzden sona ekleme ortalamada ucuzdur. Buna amortized costBir işlemin tek tek değil, uzun bir dizi boyunca ortalama maliyeti. ArrayList'e ekleme arada bir tüm diziyi kopyalar ama ortalamada sabit maliyetlidir.Sözlükte gör → denir.

Başa ya da araya eklemek ise farklı: arkadaki herkes birer koltuk kaymak zorundadır.

Kafam karıştı, daha basit anlat

ArrayList numaralı koltuklardır: 5000 numarayı söyle, doğrudan oraya gidersin. Ama en öne biri oturacaksa arkadaki herkes bir koltuk kaymak zorunda.

Hızlı kontrolBaşlangıç

`list.get(5000)` çağrısı ArrayList'te ve LinkedList'te nasıl çalışır?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

İç dizisi tamamen dolu bir ArrayList'e `add(x)` çağrıldığında ne olur?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

LinkedList: düğümler ve yürüyüş

64 elemanlı bir LinkedList'te get(32) kaç düğüm yürür? Cevabı göster

31. LinkedList indeksi saklamaz; yakın uçtan başlayıp düğüm düğüm ilerler. Aynı okuma ArrayList’te tek adımdır.

Her eleman, iki komşusunun elini tutan ayrı bir kişidir (düğüm). Araya birini sokmak için iki eli bırakıp yeniden tutmak yeter; ama doğru yeri bulmak için baştan saymak gerekir.

Kitaplarda yazan “araya ekleme çok ucuzdur” cümlesi, yalnızca o yere zaten gelmişsen doğrudur. add(i, x) önce oraya kadar yürümek zorundadır.

Kafam karıştı, daha basit anlat

LinkedList el ele tutuşan bir zincirdir. Araya birini sokmak kolaydır, ama doğru yeri bulmak için baştan tek tek saymak gerekir.

Hızlı kontrolOrta

"LinkedList'te araya ekleme O(1)" cümlesi ne zaman doğrudur?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Kendin gör

ArrayList ve LinkedList — bir işlem kaç adım?

Tohum 882522

ArrayList — 12 eleman, iç dizi kapasitesi 15

  1. 0. eleman
  2. 1. eleman
  3. 2. eleman
  4. 3. eleman
  5. 4. eleman
  6. 5. eleman
  7. 6. eleman
  8. 7. eleman
  9. 8. eleman
  10. 9. eleman
  11. 10. eleman
  12. 11. eleman
Hız
Adım 0

Şu an ne oldu?

ArrayList, 12 eleman: sona ekle — add(x)

Adım: kopyalanan eleman, izlenen düğüm ya da ayrılan yeni nesne. Süre değil, iş miktarı.

Görevler0/4

  • ArrayList'e 40+ adım yaptıran tek bir ekleme bulaçık

    İpucu

    Sona eklemek ucuz. Hangi ekleme her elemanı yerinden oynatır?

  • LinkedList'e 20+ düğüm yürüten bir okuma yaptıraçık

    İpucu

    LinkedList'te indeksle okumak, yakın uçtan başlayıp düğüm düğüm ilerler.

  • ArrayList'in iç dizisini büyütaçık

    İpucu

    Büyüme yalnızca dizi tam doluyken olur. Kapasiteler: 10, 15, 22, 33, 49...

  • Tek thread'le ConcurrentModificationException alaçık

    İpucu

    Bir for-each döngüsünün içinde listeyi değiştirmeyi dene.

Olay günlüğü (0)

Henüz olay yok. Oynat veya adımla.

  1. Varsayılanla oynat. ArrayList’e sona ekleme: tek yazma.
  2. Boyutu 15 yap. Dizi dolu; 15 eleman kopyalandı.
  3. “Başa ekle” seç, boyutu 40 yap. 40 eleman kaydı.
  4. LinkedList’e geç. Aynı ekleme tek bir düğüm ayırıyor.
  5. “Ortadakini oku” seç. Bu kez LinkedList yürüyor, ArrayList tek adımda.
Hızlı kontrolOrta

Big-O'ya göre LinkedList'in avantajlı olması gereken bazı iş yüklerinde bile ArrayList ölçümde daha hızlı çıkıyor. En önemli sebep?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Satır satır: gezinirken silmek

Tek thread, yine de exception

Cleanup.java
1List<String> names = new ArrayList<>(List.of("ali", "", "can", "ece"));
2
şu an çalışan satırfor (String name : names) {
4 if (name.isBlank()) {
5 names.remove(name);
6 }
7}

Debug

Adım 1/4

for-each Gizli bir Iterator açılır ve listenin değişiklik sayacını ezberler.

modCount
= 0
expectedModCount
= 0
Java 21UTF-8LF3:1

Sol/sağ ok tuşlarıyla da gezebilirsin.

Buna fail-fast iteratorGezildiği sırada koleksiyon kendi dışından değiştirilirse bunu fark edip hemen ConcurrentModificationException fırlatan iterator. Tek thread'de de çalışır.Koleksiyon her yapısal değişiklikte modCount'u artırır; iterator her next() çağrısında kendi beklediği değerle karşılaştırır. Bu bir en-iyi-çaba denetimidir, eşzamanlılık garantisi değildir.Sözlükte gör → denir: liste, sen gezinirken değişti mi diye bakar ve değiştiyse hemen durur. Birden çok thread gerekmez; kural şu: gezinirken listeyi yalnızca iterator değiştirebilir.

Doğrusu
names.removeIf(String::isBlank); // tek geçiş, kaydırma bir kez
Kafam karıştı, daha basit anlat

Bir listeyi gezerken ona eleman ekleyip çıkarma. Silmen gerekiyorsa removeIf kullan: o, gezmeyi ve silmeyi senin yerine güvenle yapar.

Hızlı kontrolOrta

Bu metot iptal edilmiş siparişleri temizlemek için yazıldı ama tek thread'de bile ConcurrentModificationException fırlatıyor. Hangi satır?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Hatalı satıra dokun, sonra kontrol et.

Cleanup.java
Java 21UTF-8LF

Tuzaklar

Sayı listesinden remove(1). Bu çağrı “1 değerini sil” değil, “1 numaralı sıradakini sil” demektir. Değeri silmek için remove(Integer.valueOf(1)) yaz.

List.of ve Arrays.asList. İlki hiç değiştirilemez, ikincisinin boyutu sabittir. İkisine de add dersen UnsupportedOperationException alırsın.

Kuyruk için LinkedList. ArrayDeque aynı işi, her eleman için ayrı bir düğüm yaratmadan ve daha hızlı yapar.

Kaç eleman geleceğini biliyorsan söyle. new ArrayList<>(n) salonu baştan büyük açar, taşınmaların hiçbiri yaşanmaz.

Hızlı kontrolOrta

Program ne yazdırır?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.
RemoveTrap.java
1List<Integer> ids = new ArrayList<>(List.of(10, 20, 1));
2
3ids.remove(1);
4System.out.println(ids);
5
6ids.remove(Integer.valueOf(1));
7System.out.println(ids);
Java 21UTF-8LF

Her satırı çalışıp çalışmayacağına göre ayır.

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Sınıflandırılmamış

Çalışır

Liste bu işleme izin veriyor

    Exception fırlatır

    UnsupportedOperationException ya da ConcurrentModificationException

      Aşağıdaki örnek bir bankadan ve koleksiyon seçimini gerçek işlere göre yapıyor: LRU cache, doğru kapasite, enum anahtarlar, kademeli faiz için sıralı sorgular ve iterasyon sırasında güvenli silme.

      Derinleş · Bankada koleksiyonlar: doğru yapı, doğru iş 5 dosya · ~86 satır · ilk okumada atlayabilirsin
      Proje dosyaları

      src/main/java/bank/coll/ LruCache.java LinkedHashMap erişim sırasıyla: üç satırda bir LRU cache (IBAN'dan alıcı adına).

      src/main/java/bank/coll/LruCache.java
      public class LruCache<K, V> extends LinkedHashMap<K, V> {
      private final int capacity;
      public LruCache(int capacity) {
      super(16, 0.75f, true); // accessOrder = true: get() moves an entry to the end
      this.capacity = capacity;
      }
      @Override
      protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
      return size() > capacity; // evict the least recently used entry after each put
      }
      // Not thread-safe: wrap it, or use Caffeine when several threads share it.
      }

      src/main/java/bank/coll/ Choices.java Seçimler: kapasiteyle açılan ArrayList, bekleyen provizyonlar için ArrayDeque, transfer durumlarına EnumMap.

      src/main/java/bank/coll/Choices.java
      public class Choices {
      // The final size is known: allocate once instead of growing (copying) repeatedly.
      static List<Statement> statementsFor(List<Account> accounts) {
      List<Statement> result = new ArrayList<>(accounts.size());
      for (Account account : accounts) result.add(Statement.of(account));
      return result;
      }
      // ArrayDeque: a contiguous ring buffer. Faster than LinkedList as a queue or a stack,
      // and without a node object per element.
      static Deque<PendingAuthorization> pendingAuthorizations() {
      return new ArrayDeque<>();
      }
      // EnumMap: an array indexed by ordinal. No hashing, iteration in declaration order.
      static Map<TransferStatus, Integer> countByStatus(List<Transfer> transfers) {
      Map<TransferStatus, Integer> counts = new EnumMap<>(TransferStatus.class);
      for (Transfer transfer : transfers) counts.merge(transfer.status(), 1, Integer::sum);
      return counts;
      }
      }

      src/main/java/bank/coll/ InterestTiers.java TreeMap: kademeli mevduat faizi; bakiyenin düştüğü kademe tek çağrıda.

      src/main/java/bank/coll/InterestTiers.java
      // A tiered deposit product: the annual rate depends on the balance tier.
      // The rates here are illustrative; the real table comes from the treasury.
      public class InterestTiers {
      // Sorted by key: floor/ceiling/subMap in O(log n), which a HashMap cannot do.
      private final NavigableMap<BigDecimal, BigDecimal> tiers = new TreeMap<>(Map.of(
      new BigDecimal("0"), new BigDecimal("0.30"),
      new BigDecimal("100000"), new BigDecimal("0.38"),
      new BigDecimal("1000000"), new BigDecimal("0.42")));
      public BigDecimal rateFor(BigDecimal balance) {
      return tiers.floorEntry(balance).getValue(); // greatest threshold <= balance
      }
      public SortedMap<BigDecimal, BigDecimal> from(BigDecimal min) {
      return tiers.tailMap(min, true);
      }
      }

      src/main/java/bank/coll/ Iteration.java Fail-fast iterator: iptal edilen harcamaları dolaşırken silmek ConcurrentModificationException; doğru yol removeIf.

      src/main/java/bank/coll/Iteration.java
      public class Iteration {
      // Reversed card transactions must not appear on the statement.
      static void removeReversedBroken(List<CardTransaction> txs) {
      for (CardTransaction tx : txs) {
      if (tx.status() == TxStatus.REVERSED) {
      txs.remove(tx); // ConcurrentModificationException on the next iteration step
      }
      }
      }
      static void removeReversed(List<CardTransaction> txs) {
      txs.removeIf(tx -> tx.status() == TxStatus.REVERSED); // one pass, no CME
      }
      }

      src/main/java/bank/coll/ Main.java Hepsini çalıştıran program.

      src/main/java/bank/coll/Main.java
      public class Main {
      public static void main(String[] args) {
      // Recipient-name lookups while the customer types an IBAN: recent ones stay hot.
      var names = new LruCache<String, String>(2);
      names.put("TR11", "Ayşe Y.");
      names.put("TR22", "Mehmet K.");
      names.get("TR11"); // TR11 is now the most recent
      names.put("TR33", "Can D."); // evicts TR22, the least recently used
      System.out.println(names.keySet()); // [TR11, TR33]
      var tiers = new InterestTiers();
      System.out.println(tiers.rateFor(new BigDecimal("250000"))); // 0.38
      System.out.println(tiers.rateFor(new BigDecimal("1000000"))); // 0.42
      }
      }

      Kendini sına

      Şimşek turu1/5

      ArrayList'te get(i) elemanı baştan sayarak bulur.

      Soru 1/2İleri

      `[a, b, c]` listesinde for-each içinde `b` silindiğinde exception fırlamıyor, ama `c` hiç işlenmiyor. Neden?

      Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

      Aklında kalacak üç şey

      1. 1 ArrayList'te okumak tek adımdır, sona eklemek de çoğu zaman ucuzdur. Başa ya da araya eklemek her elemanı kaydırır.
      2. 2 LinkedList'te araya girmek ucuzdur ama doğru yeri bulmak baştan yürümek demektir. Pratikte ArrayList ve ArrayDeque çoğu zaman daha hızlıdır.
      3. 3 for-each içinde listeden eleman silmek tek thread'de bile hata fırlatır. Silmek için removeIf ya da iterator.remove kullanılır.
      Sonraki kapı finally bloğunda return yazarsan, fırlatılan hata nereye gider? Exception Yönetimi — Hata Yukarı Çıkarken Ne Kaybolur · 8 dk

      5 kart sonraki derste seni bekliyor

      0/5 kart bu dersten toplandı