简体   繁体   中英

Java PriorityQueue custom Comparator

In my PriorityQueue I have 2 types of customers, VIP and regular. I want to serve VIP first, then regular.

If CustomerID < 100 it is considered to be VIP.

If a customer is VIP he goes at the end of VIP part of the queue

If a customer is regular he goes at the end of the entire queue.

In other words, I want to sort by boolean VIP value, while preserving the order in which customers came in.

Here's my Order class

public class Order implements Comparable<Order> {
    private final int customerID;
    private final int amount;
    private final boolean vip_status;

    public Order(int customerID, int amount) { 
        this.customerID = customerID;
        this.amount = amount;
        this.vip_status = customerID < 100 ? true : false;

    }

    @Override
    public int compareTo(Order o) {
        if (vip_status && !o.vip_status) {
            return -1;
        }
        if (!vip_status && o.vip_status)
            return 1;
        return 0;
    }

    public int getCustomerID() {
        return customerID;
    }

    public int getAmount() {
        return amount;
    }

    public boolean isVip_status() {
        return vip_status;
    }
}

Here's my attempt to fill the queue:

import java.util.PriorityQueue;

public class MyPriorityQueue {
    public static void main(String[] args) {
        PriorityQueue<Order> queue = new PriorityQueue<>();
        Order o1 = new Order(1, 50);
        Order o2 = new Order(5, 30);
        Order o3 = new Order(4, 10);
        Order o4 = new Order(150, 5);
        Order o5 = new Order(2, 5);
        Order o6 = new Order(200, 5);

        queue.add(o1);
        queue.add(o2);
        queue.add(o3);
        queue.add(o4);
        queue.add(o5);
        queue.add(o6);

        while(!queue.isEmpty()){
            Order s = queue.poll();
            System.out.printf("VIP Status: %s CustomerID: %s Amount: %s%n", 
                        s.isVip_status(), s.getCustomerID(), s.getAmount());
        }
    }
}

RESULT that I'm getting (which is wrong):

VIP Status: true CustomerID: 1 Amount: 50
VIP Status: true CustomerID: 5 Amount: 30
VIP Status: true CustomerID: 2 Amount: 5
VIP Status: true CustomerID: 4 Amount: 10
VIP Status: false CustomerID: 150 Amount: 5
VIP Status: false CustomerID: 200 Amount: 5

This is what I expected to see (CustomerID 2 and 4 should be in the same order they came in):

VIP Status: true CustomerID: 1 Amount: 50
VIP Status: true CustomerID: 5 Amount: 30
VIP Status: true CustomerID: 4 Amount: 10
VIP Status: true CustomerID: 2 Amount: 5
VIP Status: false CustomerID: 150 Amount: 5
VIP Status: false CustomerID: 200 Amount: 5

UPDATE: I don't want sort by any other column except VIP. I don't want add "date" because it feels like a hack, rather than understanding how Java works.

It appears that the PriorityQueue class that java comes with out of the box feels free to reorder items if they compare as equal to each other .

(This is not "how java works", it is just a little perversion on behalf of a certain class that comes with the Java Runtime.)

So, here is something that will probably work:

  1. Introduce a new OrderPlacement class, containing a) an Order and b) an int priority .

  2. In your PriorityQueue add OrderPlacement objects instead of Order objects.

  3. When you create a new OrderPlacement object, issue a new priority for it, by incrementing a counter.

Then, your OrderPlacement object can have a compareTo() method that looks like this:

@Override
public int compareTo( OrderPlacement o ) 
{
    int d = -Boolean.compare( order.vip_status, o.order.vip_status );
    if( d != 0 )
        return d;
    return Integer.compare( priority, o.priority );
}

If you must do it using priority queue, below code will solve your problem. Do note that I am using a static counter to maintain correct order of elements with same VIP status as equal elements are maintained in random order inside priority queue. This is because priority queue uses a min / max heap data structure and it only cares about placing min / max element at top of heap and does not bother about ordering of same elements.

import java.util.PriorityQueue;

public class Order implements Comparable<Order> {
  private final int customerID;
  private final int amount;
  private final int vip_status;
  private final int score;
  private static int counter = 0;

  public Order(int customerID, int amount) {
    this.customerID = customerID;
    this.amount = amount;
    this.vip_status = customerID < 100 ? 0 : 1;
    this.score = counter++;
  }

  @Override
  public String toString() {
    return customerID + " : " + amount + " : " + vip_status;
  }

  @Override
  public int compareTo(Order o) {
    int status = ((Integer) this.vip_status).compareTo(o.vip_status);
    status = status == 0 ? ((Integer) this.score).compareTo(o.score) : status;
    return status;
  }

  public static void main(String[] args) {
    Order o1 = new Order(1000, 100);
    Order o2 = new Order(500, 100);
    Order o3 = new Order(99, 100);
    Order o4 = new Order(10, 100);
    Order o5 = new Order(200, 100);
    Order o6 = new Order(1, 100);

    PriorityQueue<Order> orderQueue = new PriorityQueue<>();
    orderQueue.offer(o1);
    orderQueue.offer(o2);
    orderQueue.offer(o3);
    orderQueue.offer(o4);
    orderQueue.offer(o5);
    orderQueue.offer(o6);

    System.out.println(orderQueue.poll());
    System.out.println(orderQueue.poll());
    System.out.println(orderQueue.poll());
    System.out.println(orderQueue.poll());
    System.out.println(orderQueue.poll());
    System.out.println(orderQueue.poll());
  }
}

`

Sample Output:

99 : 100 : 0
10 : 100 : 0
1 : 100 : 0
1000 : 100 : 1
500 : 100 : 1
200 : 100 : 1

Note: you need to be aware that score may eventually reach Integer.MAX_VALUE

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM