Priority Queue 와 Queue 의 변체 실현

33872 단어
대기 열과 우선 대기 열 은 우리 가 매우 잘 아 는 데이터 구조 이다.이른바 '선진 선발' 기능 을 제 공 했 고 우선 대열 은 특정한 규칙 에 따라 '선진 선발' 을 했다.그러나 이들 은 '고정 크기 의 대기 열' 과 '고정 크기 의 우선 대기 열' 기능 을 제공 하지 않 았 다.
예 를 들 어 우 리 는 시간 에 따라 순 위 를 매 긴 가장 가 까 운 로그 인 사이트 의 20 명 을 기록 해 야 한다.점수 순 으로 가장 높 은 30 명;예 를 들 어 게임 에서 두 개의 PK 의 전투 에서 가장 높 은 점 수 를 받 은 6 명;이러한 기능 을 실현 하려 면 필요 한 데이터 구 조 는 자바 라 이브 러 리 에 기 존 클래스 가 없습니다.우 리 는 기 존의 라 이브 러 리 를 이용 하여 그것들 을 실현 해 야 한다.
1. 고정 크기 의 '먼저 나 가기' 대기 열
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;

public class TopQueue {
    private final LinkedBlockingQueue blockQueue;
    
    public TopQueue(int size){
        this.blockQueue = new LinkedBlockingQueue(size);
    }
    
    public synchronized void put(E e) throws InterruptedException{
        if(blockQueue.offer(e)){
            return;
        }else{
            blockQueue.take();
            blockQueue.offer(e);
        }
    }
    
    public List getAll(){
        return new ArrayList(blockQueue);
    }
    
    public static void main(String[] args) throws InterruptedException{
        TopQueue tq = new TopQueue(3);
        tq.put(1);
        tq.put(2);
        tq.put(3);
        System.out.println(Arrays.toString(tq.getAll().toArray()));
        
        tq.put(4);
        System.out.println(Arrays.toString(tq.getAll().toArray()));
        
        tq.put(5);
        System.out.println(Arrays.toString(tq.getAll().toArray()));
        
        tq.put(6);
        System.out.println(Arrays.toString(tq.getAll().toArray()));
    }    
}

출력 결과:
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
[4, 5, 6]

위의 TopQueue 는 '고정 크기 의 스 레 드 안전' 대기 열 을 실현 합 니 다.몇 개의 스 레 드 가 있 든 TopQueue 에 몇 개의 요 소 를 넣 었 는 지, TopQueue 에 서 는 마지막 으로 넣 은 n 개의 요소 만 유지 합 니 다.
2. 고정 크기 의 우선 순위 대기 열 (구현 1)
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.PriorityBlockingQueue;
import com.alibaba.fastjson.JSON;

public class TopPriorityQueue {
    private final PriorityBlockingQueue blockQueue;
    private final int size;

    public TopPriorityQueue(int size){
        this.blockQueue = new PriorityBlockingQueue(size + 1);
        this.size = size + 1;    //     1      put         ,        ,    1   "  "
    }
    
    public synchronized void put(E e) throws InterruptedException{
        if(blockQueue.size() >= size)
            blockQueue.take();
        blockQueue.offer(e);
    }
    
    public List getAll() throws InterruptedException{
synchronized(this){
if(blockQueue.size() >= size) blockQueue.take(); // 1, } return new ArrayList(blockQueue); } public static void main(String[] args) throws InterruptedException{ final TopPriorityQueue tq = new TopPriorityQueue(3); User u1 = new User(1, "bbb", 10); User u2 = new User(2, "ccc", 20); User u3 = new User(3, "ddd", 30); User u4 = new User(4, "fff", 40); User u5 = new User(5, "fff", 50); User u6 = new User(6, "ddd", 60); User u7 = new User(7, "ggg", 70); User u8 = new User(8, "hhh", 80); tq.put(u4); //4 System.out.println(JSON.toJSONString(tq.getAll())); tq.put(u8); //4,8 System.out.println(JSON.toJSONString(tq.getAll())); tq.put(u7); //4,8,7 System.out.println(JSON.toJSONString(tq.getAll())); tq.put(u5); //5,8,7 System.out.println(JSON.toJSONString(tq.getAll())); tq.put(u2); //5,8,7 System.out.println(JSON.toJSONString(tq.getAll())); tq.put(u3); //5,8,7 System.out.println(JSON.toJSONString(tq.getAll())); tq.put(u1); //5,8,7 System.out.println(JSON.toJSONString(tq.getAll())); tq.put(u6); //6,8,7 System.out.println(JSON.toJSONString(tq.getAll())); } }

User 클래스:
import java.util.Comparator;

public class User implements Comparable{
    private int id;
    private String name;
    private long score;    //   
    // ... ...
    
    public User(int id, String name, long score){
        this.id = id;
        this.name = name;
        this.score = score;
    }
    
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public long getScore() {
        return score;
    }
    public void setScore(long score) {
        this.score = score;
    }

    @Override
    public int compareTo(User o) {
        return this.getScore() > o.getScore() ? 1 : this.getScore() < o.getScore() ? -1 : 0;
    }
}

입력 한 결 과 는:
[{"id":4,"name":"fff","score":40}]
[{"id":4,"name":"fff","score":40},{"id":8,"name":"hhh","score":80}]
[{"id":4,"name":"fff","score":40},{"id":8,"name":"hhh","score":80},{"id":7,"name":"ggg","score":70}]
[{"id":5,"name":"fff","score":50},{"id":8,"name":"hhh","score":80},{"id":7,"name":"ggg","score":70}]
[{"id":5,"name":"fff","score":50},{"id":8,"name":"hhh","score":80},{"id":7,"name":"ggg","score":70}]
[{"id":5,"name":"fff","score":50},{"id":8,"name":"hhh","score":80},{"id":7,"name":"ggg","score":70}]
[{"id":5,"name":"fff","score":50},{"id":8,"name":"hhh","score":80},{"id":7,"name":"ggg","score":70}]
[{"id":6,"name":"ddd","score":60},{"id":8,"name":"hhh","score":80},{"id":7,"name":"ggg","score":70}]

TopPriority Queue 는 '고정 크기 의 우선 대기 열' 을 실 현 했 습 니 다. 실현 원 리 는 다음 과 같 습 니 다.
public synchronized void put(E e) throws InterruptedException{        if(blockQueue.size() >= size)            blockQueue.take();        blockQueue.offer(e); }
대기 열 이 가득 찼 을 때, 또 삽입 해 야 할 때, 대기 열 중 가장 작은 하 나 를 삭제 한 다음 삽입 합 니 다. 그러나 여기에 문제 가 있 습 니 다. 만약 에 이 삽입 할 요소 의 우선 순위 가 삭 제 된 요소 의 우선 순위 보다 낮다 면, 그것 은 큰 것 을 삭제 하 는 것 이 아니 라 오히려 작은 것 을 삽입 하 는 것 입 니 다. 그래서 여기 서 우리 가 취 하 는 방법 은 실제 요구 하 는 size 를 바탕 으로 하 는 것 입 니 다.하 나 를 더 남 겨 두 고 '호루라기 카드' 로 사용 합 니 다. 대기 열 이 가득 찼 을 때, 우 리 는 '호루라기 카드' 를 삭제 한 후에 우리 의 요 소 를 삽입 합 니 다. 그리고 대기 열 에 있 는 새로운 가장 작은 요 소 는 새로운 '호루라기 카드' 가 됩 니 다. 그리고 '호루라기 카드' 입 니 다.가장 작은 것 이기 때문에 우리 가 필요 로 하 는 것 이 아니 라 최종 결 과 를 되 돌 릴 때 삭 제 됩 니 다. 따라서 큰 것 을 삭제 하고 작은 문 제 를 삽입 하지 않 습 니 다. 여기 에는 작은 기술 이 있 습 니 다.
 
3. 고정 크기 의 우선 대기 열 (구현 2)
위의 실현 은 우리 가 대기 열 에 있 는 요소 인 Comparable 인 터 페 이 스 를 삽입 해 야 합 니 다. 그러나 실제 환경 에서 우 리 는 이러한 수정 을 할 수 없습니다. 그래서 우 리 는 또 다른 방법 이 있 습 니 다. Comparator 를 사용 하여 해결 하고 코드 를 봅 니 다.
import java.util.Comparator;
import com.coin.User;

public class MyComparator implements Comparator {
    @Override
    public int compare(User u1, User u2) {
        if(u1.getScore() > u2.getScore())
            return 1;
        if(u1.getScore() < u2.getScore())
            return -1;
        return 0;
    }
}
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.PriorityBlockingQueue;

import com.alibaba.fastjson.JSON;

public class TopPriorityQueue {
    private final PriorityBlockingQueue blockQueue;
    private final int size;
    
    public TopPriorityQueue(int size, Comparator comparator){
        this.blockQueue = new PriorityBlockingQueue(size + 1, comparator);
        this.size = size + 1;    //     1      put         ,        ,    1   "  "
    }
    
    public synchronized void put(E e) throws InterruptedException{
        if(blockQueue.size() >= size)
            blockQueue.take();
        blockQueue.offer(e);
    }
    
    public List getAll() throws InterruptedException{
        synchronized(this){
            if(blockQueue.size() >= size)
                blockQueue.take();    //           1,      
        }
        
        return new ArrayList(blockQueue);
    }
    
    public static void main(String[] args) throws InterruptedException{
        MyComparator myComparator = new MyComparator();
        final TopPriorityQueue tq = new TopPriorityQueue(3, myComparator);
        User u1 = new User(1, "bbb", 10);
        User u2 = new User(2, "ccc", 20);
        User u3 = new User(3, "ddd", 30);
        User u4 = new User(4, "fff", 40);
        User u5 = new User(5, "fff", 50);
        User u6 = new User(6, "ddd", 60);
        User u7 = new User(7, "ggg", 70);
        User u8 = new User(8, "hhh", 80);

        tq.put(u4);    //4
        System.out.println(JSON.toJSONString(tq.getAll()));
        tq.put(u8);    //4,8
        System.out.println(JSON.toJSONString(tq.getAll()));
        tq.put(u7);    //4,8,7
        System.out.println(JSON.toJSONString(tq.getAll()));
        tq.put(u5);    //5,8,7
        System.out.println(JSON.toJSONString(tq.getAll()));
        tq.put(u2);    //5,8,7
        System.out.println(JSON.toJSONString(tq.getAll()));
        tq.put(u3);    //5,8,7
        System.out.println(JSON.toJSONString(tq.getAll()));
        tq.put(u1);    //5,8,7
        System.out.println(JSON.toJSONString(tq.getAll()));
        tq.put(u6);    //6,8,7
        System.out.println(JSON.toJSONString(tq.getAll()));
    }
}

그래서 우 리 는 Priority BlockingQueue 를 사용 할 때 우리 가 삽입 한 요소 가 Comparable 이라는 인 터 페 이 스 를 실현 하거나 Comparator 를 정의 하여 Priority BlockingQueue 의 구조 함수 에 들 어가 면 Priority BlockingQueue. offer (e) 방법의 소스 코드 를 볼 수 있 습 니 다. 이 두 가지 상황 을 판단 할 수 있 습 니 다.
public boolean offer(E e) {
        if (e == null)
            throw new NullPointerException();
        final ReentrantLock lock = this.lock;
        lock.lock();
        int n, cap;
        Object[] array;
        while ((n = size) >= (cap = (array = queue).length))
            tryGrow(array, cap);
        try {
            Comparator super E> cmp = comparator;
            if (cmp == null)
                siftUpComparable(n, e, array);
            else
                siftUpUsingComparator(n, e, array, cmp);
            size = n + 1;
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
        return true;
    }

그 중의 코드:
            Comparator super E> cmp = comparator;
            if (cmp == null)
                siftUpComparable(n, e, array);
            else
                siftUpUsingComparator(n, e, array, cmp);

Priority BlockingQueue 의 구조 함수 에 Comparator 가 들 어 왔 는 지 여 부 를 판단 하 는 것 입 니 다. 그러면 User 류 는 Comparable 인 터 페 이 스 를 실현 할 필요 가 없습니다.
 
그리고 우 리 는 링크 드 블록 링 큐 에 주의해 야 한다.  화해시키다  Priority BlockingQueue 는 조금 다 릅 니 다. BlockingQueue. offer (e) 는 대기 열 이 가득 찼 을 때 false 로 돌아 갑 니 다. Priority BlockingQueue. offer () 는 대기 열 이 가득 찼 더 라 도 확장 되 어 true 로 만 영원히 돌아 갑 니 다.
LinkedBlockingQueue .offer () 의 원본 코드 는 다음 과 같 습 니 다.
/**
     * Inserts the specified element at the tail of this queue if it is
     * possible to do so immediately without exceeding the queue's capacity,
     * returning {@code true} upon success and {@code false} if this queue
     * is full.
     * When using a capacity-restricted queue, this method is generally
     * preferable to method {@link BlockingQueue#add add}, which can fail to
     * insert an element only by throwing an exception.
     *
     * @throws NullPointerException if the specified element is null
     */
    public boolean offer(E e) {
        if (e == null) throw new NullPointerException();
        final AtomicInteger count = this.count;
        if (count.get() == capacity)
            return false;
        int c = -1;
        Node node = new Node(e);
        final ReentrantLock putLock = this.putLock;
        putLock.lock();
        try {
            if (count.get() < capacity) {
                enqueue(node);
                c = count.getAndIncrement();
                if (c + 1 < capacity)
                    notFull.signal();
            }
        } finally {
            putLock.unlock();
        }
        if (c == 0)
            signalNotEmpty();
        return c >= 0;
    }

가득 찼 을 때 false 로 돌아 가기:
if (count.get() == capacity)       return false;
 
Priority BlockingQueue. offer () 의 원본 코드 는 다음 과 같 습 니 다.
/**
     * Inserts the specified element into this priority queue.
     * As the queue is unbounded, this method will never return {@code false}.
     *
     * @param e the element to add
     * @return {@code true} (as specified by {@link Queue#offer})
     * @throws ClassCastException if the specified element cannot be compared
     *         with elements currently in the priority queue according to the
     *         priority queue's ordering
     * @throws NullPointerException if the specified element is null
     */
    public boolean offer(E e) {
        if (e == null)
            throw new NullPointerException();
        final ReentrantLock lock = this.lock;
        lock.lock();
        int n, cap;
        Object[] array;
        while ((n = size) >= (cap = (array = queue).length))
            tryGrow(array, cap);
        try {
            Comparator super E> cmp = comparator;
            if (cmp == null)
                siftUpComparable(n, e, array);
            else
                siftUpUsingComparator(n, e, array, cmp);
            size = n + 1;
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
        return true;
    }

꽉 찼 을 때 용량 을 늘 립 니 다.
while ((n = size) >= (cap = (array = queue).length))            tryGrow(array, cap);
 
As the queue is unbounded, this method will never return {@code false}.
또한 TopQueue 와 TopPriority Queue 는 모두 스 레 드 가 안전 하지만 대기 열 에 삽 입 된 요소 자체 의 스 레 드 안전성 을 보장 하지 않 습 니 다.

좋은 웹페이지 즐겨찾기