Java’da List Interface ve Queue Interface’i ve Implemantasyonları

Duygu Orhan
folksdev
Published in
8 min readMar 28, 2024
List Interface ve Queue Interface’i ve Implemantasyonları

List Interface

List interface, öğeleri sıralı bir şekilde saklayabilen, boyutu dinamik olarak değişebilen yani öğe eklendikçe boyutu artabilen silindikçe boyutu azalabilen, aynı nesnelerin birden fazla olabilmesine olanak sağlayan, her bir nesne için bellekte ayrı yer tutan, boş elemanların da saklanabildiği bir koleksiyon türüdür ve Collection interface’den miras alınmıştır. List içerisinde ilerleme foreach, iterator veya index kullanılarak gerçekleştirilebilir.

List bir interface olduğu için ve interface’lerden de direkt nesne üretilemediği için alt sınıflarından nesne oluşturarak kullanılır.

List Interface’nin 3 alt sınıfı vardır: ArrayList, Vector, LinkedList.

ArrayList ve LinkedList alt sınıfları senkronize edilemez yani aynı anda birden fazla iş parçacığı bu List’lere doğru bir şekilde erişemez. Senkronizasyon manuel olarak yapılır. Aşağıdaki şekilde kullanarak sınıfları senkronize eder ve liste içindeki işlemler doğru iş parçacığı olarak gerçekleşir, veri bütünlüğü korunur.

List<String> list = new ArrayList<String> ();  
List<String> synchronizedList = Collections.synchronziedList(list);

ArrayList

Liste halindeki aynı tür verileri bir arada tutmamızı sağlayan veri yapısıdır. ArrayList sınıfı Array’in yeniden boyutlandırılabilir halidir. İhtiyaç duyulduğunda boyutu büyüyebilir veya küçülebilir, dinamik bir yapıya sahiptir. Verilerin ekleme sırası korunur. Ekleme, silme, kontrol etme, sıralama gibi çeşitli işlemleri gerçekleştiren metotlar bulunmakta ve işimizi kolaylaştırmaktadır.


import java.util.*;

public class Main{
public static void main(String[] args) {
List<String> flowers = new ArrayList<String>();
//add metodu ile listeye öğe ekleme
flowers.add("Rose");
flowers.add("Iris");
flowers.add("Tulips");
flowers.add("Begonia");
System.out.println(flowers);

//get metodu ile indexteki veriyi çağırma
System.out.println(flowers.get(1));

//size metodu ile liste boyutunu ölçme
System.out.println("ArrayList size: "+flowers.size());

//set metodu ile indexteki veriyi güncelleme
flowers.set(2,"Lavender");

//remove metodu ile öğe silme
flowers.remove(1);
System.out.println("ArrayList size after remove: "+flowers.size());

//foreach döngüsü ile liste içini gezme
System.out.println("ArrayList:");
for(String flower: flowers){
System.out.println(flower);
}

//contains metodu ile liste içinde öğe kontrolü, sonuç boolean döner
System.out.println("Is there Lavender :" + flowers.contains("Lavender"));

}
}
Çıktı

LinkedList

Linked List en önemli özelliği 2 veriyi aynı yerde tutmasıdır: Şu anki değeri ve kendisinden sonra gelecek olan adresi. Bu özellik sayesinde veriler birbirine bağlı olmaktadır. Birbiri ardınca gelen bağlı verilere bakılırsa Linked List’i aslında bir düğüm zincirine benzetilebilir.

Linked List’in her düğümü bağlantılı olduğu düğümün adresini bildiğinden bellekte ardışık olarak yer tutmalarına gerek yoktur, düğümler bellekte başka boş buldukları alana yerleşebilirler. LinkedList’i tanımlarken de boyutunun girilmesine gerek yoktur. Ve böylelikle LinkedList’ in boyutuna da dinamik denilebilir.

LinkedList, Queue interface’ni implemente ederek bir kuyruk(queue) mantığına uygun bir yapıda listeleme şeklidir. Ve Queue FIFO (First-In-First-Out) mantığına dayalı bir veri yapısıdır, yani ilk eklenen eleman ilk çıkarılır.

Linked List’i daha detaylı incelediğim yazıma buradan ulaşabilirsiniz. 👇🏻

Vector

Temelde ArrayList sınıfı ile aynı sayılır. Aralarındaki en önemli fark Vektor’ün senkronize olarak çalışmasıdır. Ve bundan dolayı da ArrayList’e göre performans olarak yavaş çalışmaktadır.

4 farklı şekilde vektör oluşturulur.

  1. Vektor() : başlangıç kapasitesi varsayılan olarak 10 belirlenen vektör. Artış değeri manuel olarak belirtilmediği için her döngüde 2 katına çıkmaktadır.
Vector<E> v = new Vector<E>();

2. Vektor(int size) : başlangıç kapasitesi boyuta göre belirtilen vektör.

Vector<E> v = new Vector<E>(int size);

3. Vector(int size, int increment) : başlangıç kapasitesi boyuta göre ve artış değeri belirtilen vektör. Bu özellikle vektöre ekleme yapılırken taşma değerine ulaştığında vektör kapasitesinin ne kadar artırılacağı belirlenir.

Vector<E> v = new Vector<E>(int size, int increment);

4. Vector(Collection c) : var olan bir Collection sınıfı dizisini alır ve başlar, o dizinin elemanlarına sahip olur.

// Vector<E> v = new Vector<E>(Collection c);

ArrayList array = new ArrayList();
Vector vector = new Vector(array);

Vektor sınıfı ilkel veri tiplerini (int,double..) içeremez, yalnızca nesneler referans tiplerini(Integer..) içerebilir. İlkel veri tipi kullanıldığında onların nesne içerisine gömülmesi gerekmektedir.


import java.util.*;
class Main{
public static void main(String[] args) {
// Başlangıçta kapasitesi 5 olan bir Vector oluşturulur
Vector<Integer> vector = new Vector<>(5);

// Vector'e 5 adet eleman eklenir
for (int i = 1; i <= 5; i++) {
vector.add(i);
}

// Kapasite ve boyut kontrol edilir
System.out.println("Capacity: " + vector.capacity());
System.out.println("Size: " + vector.size());

// 6. eleman eklenir (kapasite otomatik olarak artar)
vector.add(6);

// Kapasite kontrol edilir
System.out.println("Capacity: " + vector.capacity());
System.out.println("Size: " + vector.size());
}
}
Çıktı

Vektor sınıfı günümüzde genellikle tercih edilmemektedir. Bunun yerine ArrayList daha çok kullanılmaktadır. Bunun sebebi de performans, maliyet, esneklik ve kullanım kolaylığıdır.

Vektor’ün doğrudan bir alt sınıfı olan Stack, Vektor sınıfından miras alınmıştır. Stack, Vector gibi çalışır ancak LIFO(Last-In-First-Out) mantığına dayalı bir veri yapısıdır. Yani son giren veri ilk çıkar. Bir stack oluşturulduğunda ilk başta boştur, hiç bir öğe içermez.

Stack’in iki temel işlemi vardır push ve pop. Push, Stack’e yeni bir eleman eklemek için kullanılır ve en üste eklenir. Pop ise Stack üstündeki elemanı çıkarmak için kullanılır.

import java.util.*;
public class Main {
public static void main(String[] args) {
// Stack (Yığın) oluşturma
ArrayDeque<Integer> stack = new ArrayDeque<>();

// push metodu ile eleman ekleme
stack.push(10);
stack.push(20);
stack.push(30);

// peek metodu ile yığının en üstündeki elemana erişme
System.out.println("Top element: " + stack.peek());

// pop metodu ile yığının en üstündeki erişme ve elemanı silme
System.out.println("Removed element: " + stack.pop());

System.out.println("New top element: " + stack.peek());
}
}
Çıktı

Stack yerine Queue’nin alt interface’i olan Deque interface’i ve implemantasyonlarını kullanmak daha çok tercih edilmektedir. Çünkü özellikle çok elemanın bulunduğu boyutu fazla olan Stack’ler performans açısından düşük kalacaktır.

Queue Interface

Queue interface, verileri işlem sırasına (kuyruk) göre tutan veri yapısıdır. Collection interface’den miras alınmıştır. FIFO(First-In-First-Out) mantığına dayalı çalışma yapısı vardır. Kuyruğa eklenen ilk eleman ilk önce işlenir. Böylelikle işlem kuyruğu için veriler sıralı bir şekilde tutulmuş olur. Queue içerisinde ilerlemek için genellikle iterator veya for-each döngüsü kullanılır. FIFO mantığına dayalı çalıştıklarından dolayı belirli bir index’teki elemanlara doğrudan erişim sağlamazlar .Queue interface’i senkronize edilebilir değildir yani aynı anda birden fazla iş parçacığının Queue’de verilere erişmesini kontrol etmek kontrol edilemez.

Queue’nin bazı temel metotları aşağıdaki kodda uygulanmıştır.

package org.example;

import java.util.*;
class Main{
public static void main(String[] args) {

Queue<Integer> q1 = new LinkedList<Integer>();
//add() metodu ile ekleme
q1.add(10);
q1.add(20);
q1.add(30);
q1.add(40);
q1.add(50);

System.out.println("Queue Elements:"+q1);
//remove() metodu ile silme işlemi
System.out.println("Remove an element: "+q1.remove());

//element() metodu ile kuyruğun başındaki elemanı getirme
System.out.println("Element at the head of the queue: "+ q1.element());

//poll() metodu ile kuyruğun başındaki elemanı getirme ve sonrasında kuyruktan silme
System.out.println("Poll(): Retrieve and remove the element at the head of the queue: "+ q1.poll());

//peek() metodu ile kuyruğun başındaki elemanı getirir ancak kaldırmaz
System.out.println("Peek(): Retrieve the element at the head of the queue: "+ q1.peek());

System.out.println("Resulting Queue:"+ q1);

}
}
Çıktı

Queue’nin bir alt sınıfı ve bir alt interface’i vardır: PriortyQueue sınıfı ve Deque interface

PriortyQueue

Normal bir Queue’den farkı verilerin önceliklerine göre sıralanmasıdır. Yani Queue’ya eklenen her verinin bir öncelik değeri vardır ve buna göre sıralanır. En yüksek önceliğe sahip veri ilk önce çıkarılır. Boş verilere de izin vermez.

7 farklı şekilde Queue oluşturulur.
1. PriorityQueue(): eklenen verileri doğal sıralamalarına göre sıralayan ve başlangıç kapasitesi 11 olarak oluşur.

PriorityQueue<E> pQueue = new PriorityQueue<E>();

2. PriorityQueue(Collection<E> c): belirtilen Collection’daki verileri içerecek şekilde oluşur. Öncelik sıralaması da koleksiyonun içindeki verilere göre doğal sıralama yapılır.

PriorityQueue<E> pQueue = new PriorityQueue<E>(Collection<E> c);

3. PriorityQueue(int startCapacity): verileri doğal sıralar ve başlangıç kapasitesi belirli şekilde oluşur.

PriorityQueue<E> pQueue = new PriorityQueue<E>(int startCapacity);

4. PriorityQueue(int startCapacity, Comparator<E> c): başlangıç kapasitesi belirtilir ve verileri Comparator’a göre sıralar. Comparator, ile kuyruk içindeki verileri karşılaştırılarak belirtilen Comparator şekline göre sıralama gerçekleştirerek oluşur.

PriorityQueue<E> pQueue = new PriorityQueue<E>(int startCapacity, Comparator<E> PriorityQueue<E> PriorityQueue<E> comparator);

5. PriorityQueue(PriorityQueue<E> c): iç içe PriorityQueue oluşturulmuştur. Böylelikle öncesinde tanımlanan PriorityQueue ile aynı verilere sahip yeni bir PriorityQueue oluşur.

PriorityQueue<E> pQueue = new PriorityQueue<E>(PriorityQueue<E> c);

6. PriorityQueue(SortedSet<E> c): belirli SortedSet’i yani sıralanmış veri kümesini kullanarak oluşur.

PriorityQueue<E> pQueue = new PriorityQueue<E>(SortedSet<E> c);

7. PriorityQueue(Comparator<E> comparator): Varsayılan başlangıç kapasitesi 11'dir ve verilerin Comparator’e göre sıralanmasıyla oluşur.

PriorityQueue<E> pQueue = new PriorityQueue<E>(Comparator<E> comparator);

Aşağıdaki kod örneğinde 7. sırada bahsettiğimiz PriorityQueue oluşturma bulunmaktadır. Comparator ile compare metodunu kullanarak PriorityQueue içersindeki verileri String uzunluklarına göre sıralayacak ve getirecektir.


import java.util.*;
public class Main {
public static void main(String[] args) {
// String uzunluklarına göre Comparator tanımlama
// Comparator ile karşılaştırma yapma
Comparator<String> lengthComparator = new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return Integer.compare(s1.length(), s2.length());
}
};

// Comparator kullanarak PriorityQueue oluşturma
PriorityQueue<String> pqQueue = new PriorityQueue<>(lengthComparator);

// Eleman ekleme
pqQueue.offer("Rose");
pqQueue.offer("Iris");
pqQueue.offer("Tulips");
pqQueue.offer("Begonia");

// PriorityQueue'dan elemanları çıkararak yazdırma
while (!pqQueue.isEmpty()) {
System.out.println(pqQueue.poll());
}
}
}
Çıktı

Deque Interface

“Çift uçlu kuyruk” anlamına gelen Deque interface’i sıradan bir Queue yapısından farklı olarak kuyruğun hem başına hem de sonuna öğe ekleyebilen ve çıkarabilen bir veri yapısıdır. Genellikle bir kuyruğu her iki yönden de erişilmek istenildiğinde kullanılır. Deque, Queue’nin genişletilmiş hali olduğu için FIFO yapısını kullanır ancak aynı zamanda bir Stack gibi LIFO yapısını da kullanabilmektedir.

Deque’nin alt iki sınıfı vardır: ArrayDeque ve LinkedList.

LinkedList sınıfı, listeleme işlemlerinin yanı sıra çift yönlü listeleme yapmaktadır ve bu çift yönlü işlemleri Deque interface’ni kullanarak gerçekleştirmektedir. LinkedList konusu yukarıda List başlığı içerisinde bahsedilmiştir. Yukarıda ilgili başlıktan daha detaylı inceleyebilirisiniz.

ArrayDeque

ArrayQueue, Array tabanlı veri yapısını kullanarak Deque interface’ni genişletmiştir. Kuyruğun hem başına hem de sonuna öğe ekleyebilir ve çıkarabilir. Array’den farkı dinamik olarak kuyruk büyür ve küçülür.

ArrayDeque, LinkedList’ten farklı olarak elemanlara doğrudan erişim sağlar ve ekleme, çıkarma işlemleri hızlıdır. LinkedList’de düğümler bir ayrı nesne olarak tutulduğu için bellek kullanımı da ArrayDeque’ye göre yüksektir. LinkedList kullanımı listenin ortasında eleman ekleme ve çıkarma gerektiren durumlarda ve yoğun işlem akışı olan durumlarda tercih edilirken ArrayDeque hızlı ekleme ve çıkarma işlemlerinde ve bellek kısıtlaması olduğu durumlarda tercih edilmektedir.


import java.util.*;
public class Main {
public static void main(String[] args) {

// ArrayDeque oluşturma
ArrayDeque<Integer> deque = new ArrayDeque<>();

// Başa ve sona eleman ekleme
deque.offerFirst(10);
deque.offer(40);
deque.offerLast(20);
deque.offerFirst(5);
deque.offer(50);
deque.offerLast(30);

// Elemanları yazdırma
System.out.println("ArrayDeque : " + deque + " Size:" + deque.size());

// Başından ve sonundan eleman çıkarma
System.out.println("First element: " + deque.pollFirst());
System.out.println("Last element: " + deque.pollLast());

// Kuyruğun güncellenmiş hali
System.out.println("Update ArrayDeque: " + deque + " Size:" + deque.size());
}
}
Çıktı

Okuduğunuz için teşekkür ederim. Umarım sizler için faydalı bir yazı olmuştur 🫶🏻

Eğer yazılarım ilginizi çekiyorsa beni takip etmeyi unutmayın ✨💐
X | LinkedIn | GitHub

Bir sonraki yazıda görüşmek üzere, sağlıcakla kalın🙌🏻

--

--