简体   繁体   中英

Sorting through an ArrayList of objects by an object's given constructor value

The focus of my question pertains to the takeInventory() method.

You can assume that InventoryDemo 's main() method is functional (aside the implementation of the takeInventory() method).

If you desire, you may find the other classes here .

The objective of my takeInventory() method is to sort through my list , and report the integer value of each unique instance of Product types.

This is to be differentiated exclusively by name:

Product( name , cost) ).

Product s of like name should be grouped together (regardless of cost).

The output should report this:

这显示了所需的输出。

I assume there is a method of sorting this data which is much more effective than my current approach. However, I do not know of one.

import java.util.*;
public class InventoryDemo
{
    public static void main(String [] args) {
        ArrayList<Product> list = new ArrayList<Product>();
        list.add(new Car("Jaguar", 1000000));
        list.add(new Car("Neon", 17000));
        list.add(new Tool("JigSaw", 149.18));
        list.add(new Car("Jaguar", 110000));
        list.add(new Car("Neon", 17500));
        list.add(new Car("Neon", 17875.32));
        list.add(new Truck("RAM", 35700));
        list.add(new Tool("CircularSaw", 200));
        list.add(new Tool("CircularSaw", 150));
        list.add(new Tool("saw1", 200));
        list.add(new Tool("saw2", 150));

        if(list.get(9).compareTo(list.get(10)) == 0) {
            System.out.println("\nBoth saws are of equal value.");
        } else if(list.get(9).compareTo(list.get(10)) > 0) {
            System.out.println("\nThe first saw is more expensive.");
        } else {
            System.out.println("\nThe second saw is more expensive.");
        }

        takeInventory(list);
    }

    public static void takeInventory(ArrayList<Product> list) {
        int inventory[] = new int[list.size()];
        int counter = 0;
        for(Product token: list) {
            for(int x = 0; x < list.size(); x++) {
                if(token.compareTo(list.get(x)) == 0) {
                    inventory[counter] = 0;
                } else {
                    counter++;
                }
            }
        }

        for(int token : inventory) {
            System.out.println(token);
        }
    }
}

If it isn't explicitly clear:

I want a remedy for my takeInventory() method. The objective intent of this method is to sort through a given ArrayList of objects, and report the sum cost of its unique-type values. This is demonstrated clearly in the output. The last string literal of the output is produced by the conditional in my main() method. The rest is to be produced by the takeInventory() method.

I am certain my current takeInventory() is not working.

I would build a Map<String, C> where C is a helper class containing the quantity ( int ) and the cost ( double ). Iterate through the product list, and for each product:

  • If the name is not in the map, associate the name to new C(1, cost) .
  • If the name is in the map, increase the quantity associated with the name by 1 and increase the cost associated with the name by cost .

Finally, iterate through the map and print the result; then you're done.

Reference: http://docs.oracle.com/javase/7/docs/api/java/util/Map.html

Here is some code:

import java.util.*;

class Product {
    private String name;
    private double cost;

    public Product (String name, double cost) {
        this.name = name;
        this.cost = cost;
    }

    public String getName() {
        return name;
    }

    public double getCost() {
        return cost;
    }
}

class Car extends Product {
    public Car(String name, double cost) {
        super(name, cost);
    }
}

class Truck extends Product {

    public Truck(String name, double cost) {
        super(name, cost);
    }
}

class Tool extends Product {

    public Tool(String name, double cost) {
        super(name, cost);
    }   
}



class Entry {
    private int quantity = 1;
    private double cost;

    public int getQuantity() {
        return quantity;
    }

    public double getCost() {
        return cost;
    }

    public Entry(double cost) {
        this.cost = cost;
    }

    public void add (double cost) {
        quantity++;
        this.cost += cost;
    }

    @Override
    public String toString() {
        return ("Quantity = " + quantity + ", Total cost = " + cost);
    }
}


public class Inventory {

    static void takeInventory(List<Product> list) {
        Map<String, Entry> map = new HashMap<>();

        for (Product p : list) {
            Entry e = map.get(p.getName());
            if (e == null) {
                map.put(p.getName(), new Entry(p.getCost()));
            } else {
                e.add(p.getCost());
            }
        }

        for (String s : map.keySet()) {
            System.out.print(s);
            Entry e = map.get(s);
            System.out.println(" " + e);            
        }
    }

    public static void main(String [] args) {
        ArrayList<Product> list = new ArrayList<Product>();
        list.add(new Car("Jaguar", 100000));
        list.add(new Car("Neon", 17000));
        list.add(new Tool("JigSaw", 149.18));
        list.add(new Car("Jaguar", 110000));
        list.add(new Car("Neon", 17500));
        list.add(new Car("Neon", 17875.32));
        list.add(new Truck("RAM", 35700));
        list.add(new Tool("CircularSaw", 200));
        list.add(new Tool("CircularSaw", 150));
        list.add(new Tool("saw1", 200));
        list.add(new Tool("saw2", 150));

        takeInventory(list);
    }
}

Output:

saw1 Quantity = 1, Total cost = 200.0
saw2 Quantity = 1, Total cost = 150.0
CircularSaw Quantity = 2, Total cost = 350.0
RAM Quantity = 1, Total cost = 35700.0
JigSaw Quantity = 1, Total cost = 149.18
Jaguar Quantity = 2, Total cost = 210000.0
Neon Quantity = 3, Total cost = 52375.32

Similar to @Zoyd's answer, you could have a Map<String, List<Product>> and then just add each product into the map like this:

Map<String, List<Product>> productMap = new HashMap<>();
for(Product product : list) {
    if (!productMap.containsKey(product.getName())) {
        productMap.put(product.getName(), new ArrayList<Product>());
    }
    productMap.get(product.getName()).add(product);
}

Then in your print method you can just iterate through the map keyset and add up all the product costs in each list like this:

for(String productName : productMap.keySet()) {
    List<Product> products = productMap.get(productName);
    int quantity = products.size();
    double totalCost = 0.0;
    for (Product product : products) {
        totalCost += product.getCost();
    }
    System.out.println(String.format("%s: Quantity = %s, Total cost = %s", productName, quantity, totalCost));
}

So a clean way to do this is to use one of Guava's FluentIterable, which is googles class for filtering sets according to some rule. First make a predicate to define your sort:

class sorter implements Predicate<Product> {
    private final String name;
    sorter(String name){
        this.name=name
    }
    @Override
    boolean apply(Product input){
        return (input.name == this.name)
    }
}

This will return true if input.name is the same as the name given to the predicate. This works with Guava's fluentIterable class to return all the elements where the predicate is true. Eg

List<Product> cars = FluentIterable.from(list).filter(new sorter("car"))

Will produce a list of all the elements which have name "car".

Since you will iterate once for each name it might have performance issues on large sets, on the other hand, since its all read only and does not mutate the list, you could easily multithread it locally.

It is very easy to read/maintainable though.

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