0

I have a list of Items as

List<Items> list = Arrays.asList(new Items("pen",10),
                new Items("pen",8),
                new Items("Pencil",10),
                new Items("Pencil",9),
                new Items("Pencil",5),
                new Items("pen",12)
                );

My Items class:

static class Items{
        private String IName;
        private int IPrice;
//constructor, getter and setter here
}

And I want to insert each object into a PriorityBlockingQueue. It should consider first all pen and sort it based on price then all pencil based on price. So the expected output for PriorityBlockingQueue should be:

Item : pen, Price : 8
Item : pen, Price : 10
Item : pen, Price : 12
Item : Pencil, Price : 5
Item : Pencil, Price : 9
Item : Pencil, Price : 10

So how can I declare a PriorityBlockingQueue to get the above output?

private PriorityBlockingQueue<Items> queue = new PriorityBlockingQueue<Items>(10,(item1,item2)-> {
//What should be the comparator logic?
});
Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
Jince Mon
  • 1
  • 1

1 Answers1

0

Firstly, it's better to use an enum as the type of item, instead of String. In this solution for the sake of simplicity I'll go with strings.

Sidenote: class-names are usually singular nouns. The plural name Items is justifiable only if an instance of the class is meant to represent multiple domain objects, for example if there would be a field amount and each instance of Items represents a zero or more pens, pencils of a particular type.

To define a comparator, you can use Java 8 static methods.

For the first comparison, we need to find out whether an item is a "Pencil" (or not a pen).

Comparator.comparing(items -> items.getIName().equals("Pencil"))

Note that lambda expression produces a boolean value, and the natural ordering of boolean values is false -> true.

Then we need to chain the second sorting criterion using thenComparing().

It can be written either like this, by specifying the type of argument:

Comparator.comparing((Items items) -> items.getIName().equals("Pencil"))
     .thenComparing(Items::getIPrice)

Or by using so-called type witness:

Comparator.<Items, Boolean>comparing(items -> items.getIName().equals("Pencil"))
     .thenComparing(Items::getIPrice)

In case if we would not provide the generic type parameter explicitly, compiler would treat the objects consumed by the comparing() and thenComparing() as being of Object and it would be impossible to invoke methods getIName() and getIPrice().

It would happen because lambda expressions and method references, are so-called poly expressions, they are sensitive to the context in which they appear. And based on the context, compiler should infer their types. In this case, when we're chaining the methods, it becomes impossible to infer the type of each lambda and reference based solely on the resulting type of the comparator.

So, that how you can declare a BlockingQueue:

BlockingQueue<Items> queue = new PriorityBlockingQueue<>(10,
    Comparator.comparing((Items items) -> items.getIName().equals("Pencil"))
        .thenComparing(Items::getIPrice)
);

A link to Online Demo

Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
  • This doesn't work. I am getting below output: pen : 8 pen : 10 pen : 12 Pencil : 9 Pencil : 5 Pencil : 10. first I got all the pen a but not ordered – Jince Mon Jul 20 '22 at 10:58
  • @JinceMon It **does work** - see [*Online Demo*](https://www.jdoodle.com/ia/toD). If you're simply printing the contents of the priority queue - then have a misconception on how this class works. Have a look at the documentation and learn about the *Heap* data structure (priority queue is an implementation of the Heap in Java). – Alexander Ivanchenko Jul 20 '22 at 11:08