← Kursa Dön
📄 Text · 15 min

List: ArrayList ve LinkedList

List, Collections Framework'ün en çok kullanılan arayüzü. Sıralı, index'li ve duplicate'e izin veren bir koleksiyon. Elemanları ekleme sırasında tutar, her birinin bir numarası (index) vardır.

List'in iki ana implementasyonu var: ArrayList ve LinkedList. İkisi de aynı interface'i implement eder ama iç yapıları tamamen farklı. Bu da performans farklarına yol açar.

Analoji: ArrayList bir apartman gibi. Her dairenin numarası var, istediğin kata asansörle direkt gidersin. Ama kat eklemek için binanın üstüne çıkman lazım. LinkedList ise bir tren gibi. Her vagon sonrakine bağlı. Ortaya vagon eklemek kolay ama "7. vagona git" demek için baştan saymak zorundasın.

ArrayList İç Yapısı

ArrayList, arka planda bir dizi (array) kullanır. Ama normal array'den farklı olarak, dolunca otomatik büyür.

// ArrayList oluşturma
List<String> names = new ArrayList<>();        // Boş, default kapasite (10)
List<String> names2 = new ArrayList<>(50);     // Başlangıç kapasitesi 50
List<String> names3 = new ArrayList<>(List.of("Ali", "Veli")); // Var olan koleksiyondan

İç yapı nasıl çalışır:

  1. Başlangıçta 10 elemanlık bir dizi oluşturulur (default capacity)

  2. Dizi dolunca, 1.5 katı büyüklüğünde yeni dizi oluşturulur

  3. Eski elemanlar yeni diziye kopyalanır

  4. Bu "grow" operasyonu O(n) maliyetindedir

List<Integer> list = new ArrayList<>(4); // Kapasite: 4
list.add(1); // [1, _, _, _]
list.add(2); // [1, 2, _, _]
list.add(3); // [1, 2, 3, _]
list.add(4); // [1, 2, 3, 4] — dolu!
list.add(5); // Büyüme! Kapasite: 6 → [1, 2, 3, 4, 5, _]

💡 İpucu: Eğer kaç eleman ekleyeceğini biliyorsan, başlangıç kapasitesini belirle. Bu, gereksiz büyüme (resize) operasyonlarını önler.

// 10.000 eleman ekleyeceğini biliyorsan
List<String> bigList = new ArrayList<>(10000);
// Kapasite 10 → 15 → 22 → ... → 10000 yerine direkt 10000 başlar

ArrayList Temel Operasyonlar

List<String> fruits = new ArrayList<>();

// Ekleme
fruits.add("Elma");              // Sona ekle
fruits.add(0, "Muz");           // Index 0'a ekle (kaydırma gerekir)
fruits.addAll(List.of("Portakal", "Üzüm")); // Toplu ekle

// Erişim
String first = fruits.get(0);    // Index ile erişim — O(1)
int index = fruits.indexOf("Elma"); // İlk bulduğu index
int last = fruits.lastIndexOf("Elma"); // Son bulduğu index

// Güncelleme
fruits.set(1, "Kivi");          // Index 1'deki elemanı değiştir

// Silme
fruits.remove("Muz");           // Değere göre sil (ilk eşleşen)
fruits.remove(0);                // Index'e göre sil
fruits.removeIf(f -> f.startsWith("P")); // Koşula göre sil

// Diğer
int size = fruits.size();        // Eleman sayısı
boolean has = fruits.contains("Kivi"); // İçeriyor mu?
fruits.clear();                  // Hepsini sil

⚠️ Integer listede remove karışıklığı: remove(int index) ve remove(Object o) çakışır. list.remove(3) index mi yoksa değer mi?

List<Integer> nums = new ArrayList<>(List.of(10, 20, 30));
nums.remove(1);              // INDEX 1'deki elemanı siler → [10, 30]
nums.remove(Integer.valueOf(10)); // DEĞER 10'u siler → [30]

ArrayList Performans

OperasyonZamanAçıklama
get(index)O(1)Direkt erişim — en güçlü yanı
add(eleman)O(1)*Sona ekleme (*amortized — resize olursa O(n))
add(index, eleman)O(n)Ortaya ekleme — elemanları kaydırmak gerekir
remove(index)O(n)Ortadan silme — elemanları kaydırmak gerekir
contains(eleman)O(n)Baştan sona arama
indexOf(eleman)O(n)Baştan sona arama

ArrayList'in gücü: random access (index ile erişim). Zayıf noktası: ortaya ekleme/silme.

LinkedList İç Yapısı

LinkedList, çift yönlü bağlı liste (doubly linked list) kullanır. Her eleman (node) üç şey tutar: değer, önceki node'un referansı, sonraki node'un referansı.

null ← [Elma] ⇄ [Armut] ⇄ [Muz] → null
        head                    tail
List<String> linked = new LinkedList<>();
linked.add("Elma");
linked.add("Armut");
linked.add("Muz");

// LinkedList'e özel method'lar (Deque interface'inden)
LinkedList<String> ll = new LinkedList<>(List.of("A", "B", "C"));
ll.addFirst("X");       // Başa ekle
ll.addLast("Z");        // Sona ekle
String first = ll.getFirst(); // İlk eleman
String last = ll.getLast();   // Son eleman
ll.removeFirst();        // Baştan sil
ll.removeLast();         // Sondan sil

LinkedList Performans

OperasyonZamanAçıklama
get(index)O(n)Baştan veya sondan sayarak ilerlemek gerekir
add(eleman)O(1)Sona ekleme — tail pointer'ı güncelle
addFirst(eleman)O(1)Başa ekleme — head pointer'ı güncelle
add(index, eleman)O(n)Index'e ulaşmak O(n), ekleme O(1)
remove(index)O(n)Index'e ulaşmak O(n), silme O(1)
contains(eleman)O(n)Baştan sona arama

LinkedList'in gücü: başa/sona ekleme-silme O(1). Zayıf noktası: index ile erişim O(n).

ArrayList vs LinkedList: Ne Zaman Hangisi?

// SENARYO 1: Çok okuma, az yazma → ArrayList
// Örn: Ürün listesi görüntüleme
List<Product> products = new ArrayList<>();
// products.get(42) → O(1), süper hızlı

// SENARYO 2: Sürekli başa/sona ekleme-silme → LinkedList
// Örn: Mesaj kuyruğu
LinkedList<Message> queue = new LinkedList<>();
queue.addLast(newMessage);    // O(1)
queue.removeFirst();           // O(1)

// SENARYO 3: Ortadan çok silme → Hiçbiri mükemmel değil
// ArrayList: silme O(n), ama cache-friendly
// LinkedList: node'a ulaşmak O(n), silme O(1)
// Pratikte: ArrayList genelde yine kazanır (CPU cache etkisi)

💡 Gerçek: Pratikte neredeyse her zaman ArrayList kullan. LinkedList'in teorik avantajı, modern CPU'ların cache yapısı nedeniyle genellikle gerçekleşmez. LinkedList'i sadece Queue/Deque olarak kullanırken tercih et.

SubList

List'in bir bölümünü "view" olarak alabilirsin:

List<Integer> nums = new ArrayList<>(List.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9));

List<Integer> sub = nums.subList(2, 6); // [2, 3, 4, 5] — index 2 dahil, 6 hariç
System.out.println(sub);

sub.set(0, 99); // sub'ı değiştirmek orijinali de değiştirir!
System.out.println(nums); // [0, 1, 99, 3, 4, 5, 6, 7, 8, 9]

sub.clear(); // sub'ı temizlemek orijinalden de siler
System.out.println(nums); // [0, 1, 5, 6, 7, 8, 9]

subList bir kopya değil, görünüm (view). Değişiklikler orijinal listeye yansır.

Sıralama

List<String> names = new ArrayList<>(List.of("Zeynep", "Ali", "Mehmet", "Canan"));

// Doğal sıralama (Comparable)
Collections.sort(names);                    // [Ali, Canan, Mehmet, Zeynep]
names.sort(null);                           // Aynı şey
names.sort(Comparator.naturalOrder());      // Aynı şey

// Ters sıralama
names.sort(Comparator.reverseOrder());      // [Zeynep, Mehmet, Canan, Ali]

// Özel sıralama — uzunluğa göre
names.sort(Comparator.comparingInt(String::length));
// [Ali, Canan, Zeynep, Mehmet]

// Sayılar
List<Integer> nums = new ArrayList<>(List.of(5, 2, 8, 1, 9));
Collections.sort(nums); // [1, 2, 5, 8, 9]

List.of() ve List.copyOf() — Immutable Listeler

// List.of() — değiştirilemez liste (Java 9+)
List<String> immutable = List.of("A", "B", "C");
// immutable.add("D");     // UnsupportedOperationException!
// immutable.set(0, "X");  // UnsupportedOperationException!
// immutable.remove(0);    // UnsupportedOperationException!

// List.copyOf() — var olan listeyi kopyalayarak immutable yap (Java 10+)
List<String> original = new ArrayList<>(List.of("X", "Y"));
List<String> copy = List.copyOf(original);
original.add("Z"); // copy ETKİLENMEZ

// Arrays.asList() — sabit boyutlu ama değiştirilebilir
List<String> fixed = Arrays.asList("A", "B", "C");
fixed.set(0, "X");   // OK — eleman değiştirme
// fixed.add("D");    // UnsupportedOperationException! — boyut değişmez
MethodDeğiştirilebilir mi?Boyut değişir mi?null izin?
new ArrayList<>()
Arrays.asList()✅ (set)❌ (add/remove)
List.of()
List.copyOf()
Collections.unmodifiableList()

List ile Yaygın Pattern'lar

// Filtreleme (Java 8+ Stream)
List<String> longNames = names.stream()
    .filter(n -> n.length() > 3)
    .collect(Collectors.toList());

// Dönüştürme
List<Integer> lengths = names.stream()
    .map(String::length)
    .collect(Collectors.toList());

// Birleştirme
String joined = String.join(", ", names); // "Ali, Veli, Ahmet"

// Kopyalama
List<String> copy = new ArrayList<>(original); // Shallow copy

// Boş liste kontrolü
if (list != null && !list.isEmpty()) {
    // Güvenli
}

Özet

  • ArrayList arka planda dizi kullanır — get(index) O(1) ile en güçlü yönü, ortadan ekleme/silme O(n) ile zayıf yönü

  • LinkedList çift yönlü bağlı liste — başa/sona ekleme O(1), ama index erişimi O(n)

  • Pratikte ArrayList neredeyse her zaman tercih edilir — CPU cache etkisi LinkedList'in teorik avantajını siler

  • List.of() ve List.copyOf() ile immutable listeler oluşturabilirsin (Java 9+/10+)

  • Integer listede remove() karışıklığına dikkat — remove(1) index'tir, remove(Integer.valueOf(1)) değerdir

  • Değişkeni List<> tipinde tanımla, implementasyonu (ArrayList/LinkedList) sağ tarafta seç — esneklik sağlar