简体   繁体   中英

How to use stream#sorted() to sort by the product of two fields in Java 8

I am new to using lambdas in Java 8 and I am having trouble with sorting.

Basically, I have 4 fields in a class, then in my main method I create an array of objects to sort. I want to be able to use Stream#sorted() to sort my output by the product of 2 fields ( price * quantity ).

This is the class I want to sort:

public class Invoice {

   private final int partNumber; 
   private final String partDescription;
   private int quantity;
   private double price;

   // constructor
   public Invoice(int partNumber, String partDescription, int quantity, double price) {
      if (quantity < 0) // validate quantity
         throw new IllegalArgumentException("Quantity must be >= 0");

      if (price < 0.0) // validate price
         throw new IllegalArgumentException("Price per item must be >= 0");

      this.partNumber = partNumber;
      this.partDescription = partDescription;
      this.quantity = quantity;
      this.price = price;
   } // end constructor

//get and set accessors 

}

Here is my test class with my array of objects:

public class ProcessInvoices  {

    public static void main(String[] args) {

      //declare array of invoice objects
      Invoice[] invoices = {
        new Invoice(83, "Electric sander", 7, 57.98),
        new Invoice(24, "Power saw", 18, 99.99),
        new Invoice(7, "Sledge hammer", 11, 21.50),
        new Invoice(77, "Hammer", 76, 11.99),
        new Invoice(39, "Lawn mower", 3, 79.50),
        new Invoice(68, "Screw driver", 106, 6.99),
        new Invoice(56, "Jig saw", 21, 11.00),
        new Invoice(3, "Wrench", 34, 7.50)};

      System.out.println("\nInvoices mapped to description and invoice amount");
      Arrays.stream(invoices)
        .sorted(Comparator.comparing(invoice.getQuantity() * invoice.getPrice()))
        .map(invoice -> String.format("Description: %-15s     Invoice amount:  $%,6.2f", invoice.getPartDescription(), (invoice.getQuantity() * invoice.getPrice())))
        .forEach(invoice -> System.out.printf("%s%n", invoice));

}

In the stream I map the partDescription to the product of quantity and price which gives the total price for an invoice. This is what I want to sort by, the total price of the invoice, but I do not know the correct way of doing this with the sorted() method.

I try to simply compare by quantity * price but that tells me that the variable "invoice is not recognized" . If I try to sort after the map() statement, that does not work either. I have also tried using a different variable, amount , no luck there either.

How do I sort by the product of two fields by using sorted() ?

You have an error in your comparator lambda, it should read

.sorted(Comparator.comparing(invoice -> invoice.getQuantity() * invoice.getPrice()))

rather than

.sorted(Comparator.comparing(invoice.getQuantity() * invoice.getPrice()))

notice missing invoice ->

I would strongly recommend you to create a method on Invoice which does the calculation:

public double getTotalPrice() {
  return quantity * price;
}

This is essantially just the basic object oriented way to do it.

You could then use this as a method reference and avoid having the logic used for sorting inside the lambda:

.sorted(Comparator.comparingDouble(Invoice::getTotalPrice))

Note that you could use the built-in comparator for sorting doubles.


As a side note, you might want to use BigDecimal if you want to have an exact floating point precision .

This will also do it:

public class InvoiceTest {
    @Test
    public void testName() throws Exception {
        //declare array of invoice objects
        Invoice[] invoices = {
            new Invoice(83, "Electric sander", 7, 57.98),
            new Invoice(24, "Power saw", 18, 99.99),
            new Invoice(7, "Sledge hammer", 11, 21.50),
            new Invoice(77, "Hammer", 76, 11.99),
            new Invoice(39, "Lawn mower", 3, 79.50),
            new Invoice(68, "Screw driver", 106, 6.99),
            new Invoice(56, "Jig saw", 21, 11.00),
            new Invoice(3, "Wrench", 34, 7.50)};

        System.out.println("\nInvoices mapped to description and invoice amount");
        Arrays.stream(invoices).sorted(new Comparator<Invoice>() {
            @Override
            public int compare(Invoice i1, Invoice i2) {
                return Double.valueOf(i1.getQuantity() * i1.getPrice()).compareTo(Double.valueOf(i2.getQuantity() * i2.getPrice()));
           }
        }).map(invoice -> String.format("Description: %-15s     Invoice amount:  $%,6.2f", invoice.getPartDescription(), (invoice.getQuantity() * invoice.getPrice())))
            .forEach(invoice -> System.out.printf("%s%n", invoice));

    }
}

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