Mars TUA - Java PriorityQueue Kullanımı
PriorityQueue Nedir?
Bir Java Collections Framework üyesi olan PriorityQueue, içerdiği elemanları öncelik sırasına göre sıralayan bir kuyruk (Queue) aracıdır.
Bu yazımızda Java'da Priority Queue yapısını inceleyeceğiz.
Normalde kuyruklar (Queue) First-in-First-Out yani ilk giren ilk çıkar mantığıyla çalışır. Ama bazen, bazı elemanların diğerlerine nazaran daha ehemmiyetli olduğu ve daha önce işleme sokulması gerektiği durumlar olabilir. Örneğin, bir toplulukta aşı olacak bir sonraki kişi seçilirken sıraya ilk gireni değil de yaşı en ileride olan kişiyi seçmek isteyebiliriz. Bu gibi durumlarda PriorityQueue sınıfını kullanabiliriz.
PriorityQueue Nasıl Sıralar?
java.util.PriorityQueue sınıfı içerdiği nesneleri doğal sırasına göre sıralar. Ayrıca istenirse bir Comparable nesnesi de verilerek sıralama özelleştirilebilir. Nesneleri sıralama mantığıyla çalıştığı için null eleman veya sıralanamayan (Non-Comparable) eleman eklenmesine izin vermez.
PriorityQueue ile En Öncelikli Elemana Nasıl Ulaşılır?
PriorityQueue kuyruğunun en başındaki eleman en öncelikli elemandır. Bu elemana peek(), element(), poll() veya remove() metodları ile ulaşabiliriz.
peek() ve element() metodları ile kuyruğun başındaki elemana kuyruktan silinmeden ulaşılır. element()'in peek()'den farkı dönecek eleman yoksa exception fırlatmasıdır.
poll() ve remove() metodları ile ise, kuyuruğun başındaki elemama kuyruktan silinerek ulaşılır. remove() metodunun farkı, dönecek eleman yoksa exception fırlatmasıdır.
PriorityQueue Elemanları Nasıl Dolaşılır? (Iteration)
Bir Java Collections sınıfı olduğundan iterator() metodu bulunmaktadır. Yalnız, gözden kaçırırmaması gereken bir nokta: iterator() metodu ile elemanlar dolaşılırken (traverse), örneğin for each yapısı ile, dolaşılan elemanlar öncelik sırasına göre olmayabilir. PriorityQueue sınıfı iterator ile döndüğü nesnelerin sıralı dönmesini garanti etmemektedir. İlla sıralı dolaşılmak isteniyorsa Arrays.sort(pq.toArray()) şeklinde oluşturulacak array üzerinde dolaşılabilir.
PriorityQueue Thread-Safe Mi?
PriorityQueue sınıfı thread-safe değildir. Birden fazla threadin aynı anda kuyruğa nesne eklemeye veya çıkarmaya çalışması durumunda java.util.ConcurrentModificationException hatası fırlatılır.
Thread-safe bir PriorityQueue kullanmak gerekiyorsa java.util.concurrent.PriorityBlockingQueue sınıfı kullanılmalıdır.
Mars Savunma Örneği
Gelelim eğlenceli bölüme. Sene 2071. Türkiye Uzay Ajansı (TUA) ve TSK Uzay Kuvvetleri Komutanlığı Mars'ta yeni kurulan Türkiye kolonisini uzaydan gelebilecek asteroitlere karşı savunma görevi için yerleştirdiği lazer silahını denemek amacıyla tatbikat düzenliyorlar.
Sizin göreviniz lazer silahınnın, komuta merkezine yaklaşan en yakın asteroidi hedef almasını sağlamak.
Bu senaryoyu koda dökelim. Mars'a olan uzaklığını ve yaklaşma hızını tutacağımız Asteroit.java sınıfını tanımlayalım. Ayrıca 1 saniyede bir hızı kadar hareket etmesini söyleyeceğimiz startComeClose metodunu tanımlayalım. Bu metodda Timer kullanarak periyodik olarak kod çalıştırdık. Java'da Timer ve TimerTask kullanımını daha detaylı öğrenmek için şu yazıya bakabilirsiniz.
import java.util.PriorityQueue; import java.util.Timer; import java.util.TimerTask; public class Asteroit { private int distanceToMars; private int speed; private int id; public Asteroit(int id, int distanceToMars, int speed) { this.id = id; this.distanceToMars = distanceToMars; this.speed = speed; } public void startComeClose(PriorityQueue<Asteroit> asteroitDefencePriorityQueue) { Timer timer = new Timer(); timer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { asteroitDefencePriorityQueue.remove(Asteroit.this); distanceToMars = distanceToMars - speed; System.out.println("#astreoit " + id + " distance: " + distanceToMars); // in order to reorder priorityqueue, the modified element should be removed and added asteroitDefencePriorityQueue.add(Asteroit.this); System.out.println("simulasyon peek id " + asteroitDefencePriorityQueue.peek().getId() +"\n"); } }, 0, 1000); } public int getDistanceToMars() { return distanceToMars; } public void setDistanceToMars(int distanceToMars) { this.distanceToMars = distanceToMars; } public int getId() { return id; } }
Şimdi örnek main uygulama metodumuzu yazalım. 2 adet asteroit tanımlayacağız. Biri 10000 km uzaklıkta fakat uzaklığı sabit, Mars'a yaklaşmıyor. Biri 15000 km uzaklıkta fakat saniyede 1000 km hızla yaklaşıyor.
Tanımlayacağımız PriorityQueue içine bu asteroitleri ekleyeceğiz. Her 1 saniyede bir, hareketli asteroitin uzaklığını güncelleyeceğiz. PriorityQueue kuyruğunun en başındaki, lazer silahımızın hedef alacağı Mars'a en yakın asteroit hangisi ise onun id'sini konsola yazacağız.
Çıktı olarak, bir süre hareketsiz bulunan asteroitin peek() metodu ile elde edildiğini ve en yakın olduğunu göreceğiz. Yaklaşık 6 sn sonra ise hareketli asteroitin daha yakında olduğu ve peek() metodu ile elde ettiğimiz asteroitin bu 2. eklediğimiz asteroit olduğunu göreceğiz.
import java.util.Comparator; import java.util.PriorityQueue; public class QueueExample { public static void main(String[] args) { PriorityQueue<Asteroit> asteroitDefencePriorityQueue = new PriorityQueue<>(new Comparator<Asteroit>() { @Override public int compare(Asteroit o1, Asteroit o2) { return Integer.compare(o1.getDistanceToMars(), o2.getDistanceToMars()); } }); Asteroit sabit = new Asteroit(1, 10000, 1000); Asteroit hareketli = new Asteroit(2, 15000, 1000); asteroitDefencePriorityQueue.add(sabit); asteroitDefencePriorityQueue.add(hareketli); hareketli.startComeClose(asteroitDefencePriorityQueue); } }
Programı çalıştırdığımızda çıktı şu şekilde olacaktır.
>
#astreoit 2 distance: 14000
simulasyon peek id 1
#astreoit 2 distance: 13000
simulasyon peek id 1
#astreoit 2 distance: 12000
simulasyon peek id 1
#astreoit 2 distance: 11000
simulasyon peek id 1
#astreoit 2 distance: 10000
simulasyon peek id 1
#astreoit 2 distance: 9000
simulasyon peek id 2
#astreoit 2 distance: 8000
simulasyon peek id 2
Java PriorityQueue Örnek Kod |
0 Yorumlar