I have this class:
class Product {
public double price;
public Product(double price) {
this.price = price;
}
}
And a Map:
Map<Product, Integer> products = new HashMap<>();
That contains several products added like so:
products.put(new Product(2.99), 2);
products.put(new Product(1.99), 4);
And I want to calculate the sum of all products multiple the values using streams? I tried:
double total = products.entrySet().stream().mapToDouble((k, v) -> k.getKey().price * v.getValue()).sum();
But it doesn't compile, I get “Cannot resolve method getValue()”.
I expect:
(2.99 * 2) + (1.99 * 4) = 5.98 + 7.96 = 13.94
The stream of entries needs single parameter lambda for each entry, not (k,v):
double total = products.entrySet().stream().mapToDouble(e -> e.getKey().price * e.getValue()).sum();
You can avoid the explicit creation of a doubleStream with something like:
double total = products.entrySet()
.stream()
.collect(Collectors.summingDouble(e -> e.getKey().price * e.getValue()));
Not directly related to your question, but I wouldn't use a map for what you are doing. Instead create a new class
public class ProductAmount {
private Product product;
private int amount;
public ProductAmount(Product product, int amount) {
this.product = product;
this.amount = amount;
}
public double getCombinedPrice() {
return product.price * amount;
}
}
Then you can use a List instead of a Map.
List<ProductAmount> products = Arrays.asList(
new ProductAmount(new Product(2.99), 2),
new ProductAmount (new Product(1.99), 4));
products.stream().mapToDouble(ProductAmount::getCombinedPrice).sum();
You can also do it like so.
double sum = 0;
for(Entry<Product, Integer> e : products.entrySet()) {
sum += e.getKey().price * e.getValue();
}
System.out.println(sum);
prints
13.940000000000001
But you have a fundamental flaw in your class. You don't override equals or hashCode. So you're are you using the object reference as the key. Try doing the following:
System.out.println(products.get(new Product(1.99));
It will print null since there is no entry for that reference (it's a different object than the one used to store the value 4).
And finally you should make certain your keys are immutable. Otherwise, circumstances could result in the same error.
Check out why do I need to override hashcode and equals.
And since it was mentioned in the comments, also check out what data type to use for money in java.
Related
I have a rather simple question since I'm a beginner.
I'm using a Java List class and I'm interested to know how to iterate through it and get all the object properties as a sum of numbers?
For example, I have a Product class with a price property type of int. My list is now filled with a couple of Products. Now I want to iterate through that list and get the sum of all Product prices.
How do I do that? Thank you.
One line to do it using Java streams:
public int calculateSum(List<Product> products) {
return products.stream().mapToInt(Product::getPrice).sum()
}
The explanation:
.mapToInt(Product::getPrice), is the equivalent of mapToInt(p -> p.getPrice()). Basically, from the list of products, we retrieve their prices, and end up having a stream of numbers (prices).
.sum() will just calculate the sum of integers that are within the stream, in this case a list of prices.
If you want to do it without streams, here's the code for that:
public int calculateSum(List<Product> products) {
int sum = 0;
for (Product product : products) {
sum += product.getPrice();
}
return sum;
}
I've assumed the Product class looks like the following:
public class Product {
private int price;
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
So I have this ArrayList of Product objects. What I'm doing is on click of + button, I am making an object of Product and setting all the attributes from ui; including 'quantity' and adding this object to ArrayList. Now, as I'm adding a single object on single click of + button, I'm getting duplicate Product objects with all same attributes other than, of course, 'quantity' count. If I add a product with quantity of 4, I get 4 objects of Product inside arraylist with different quantity 1,2,3 and 4. I only want to have Product object with maximum quantity inside the collection. I have used Comparator for this problem, but I am doing something wrong here. Please help find out what I'm doing wrong. Thanks. Here's the model of Product with getter and setters excluded from snippet.
Product:
private String category;
private String code;
private String description;
private String units;
private String weight;
private Integer tax;
private String pieces;
private Integer aliasFlag;
private Double price;
private Integer quantity;
private Integer taxAmount;
private Double totalAmount;
What I'm doing so far to compare two or more(as per the quantity count) objects of Product using Comparator is:
Collections.sort(mProductsToBeSent, new Comparator<Products>() {
#Override
public int compare(Products ob1, Products ob2) {
if (ob1.getCode().equals(ob2.getCode())) {
if (ob1.getQuantity()<ob2.getQuantity()){
mProductsToBeSent.remove(ob1);
}
}
return 0;
}
});
mProductToBeSent is my ArrayList that I'm gonna have to send as a Json, but since there is duplication, this won't do. I'm comparing 'Code' attribute of two subsequent objects for the same product and if it's true then I'm comparing the 'Quantity'. If it's true then I'm removing the lesser quantity object from the list. But I'm not getting desired output.
The output I'm getting right now:
[
Products{Aliasflag='0', Code ='BMA10K', Description=abc, Price=270.0, Quantity=1, Taxamount=0, Units='BAGS'},
Products{Aliasflag='0', Code ='BMA10K', Description=def, Price=270.0, Quantity=2, Taxamount=0, Units='BAGS'},
Products{Aliasflag='0', Code ='BMA10K', Description=ghi, Price=270.0, Quantity=3, Taxamount=0, Units='BAGS'},
Products{Aliasflag='0', Code ='BMA10K', Description=jkl, Price=270.0, Quantity=4, Taxamount=0, Units='BAGS'}]
As you can see, I'm trying to remove the first three objects to be deleted and only preserve the object with max quantity in the arraylist; that is 4th object--in this case.
Desired output :
[
Products{Aliasflag='0', Code ='BMA10K', Description=jkl, Price=270.0, Quantity=4, Taxamount=0, Units='BAGS'}]
The usual way to do this is not to add a different product into your list with just another quantity value, but to get the product out of the list and update its quantity.
Wrote as answer because I cannot just comment and I think it will help you.
You can do something like this :
mProductsToBeSent.stream()
.collect(Collectors.groupingBy(Product::getCode,
Collectors.maxBy(Comparator.comparing(Category::getQuantity))));
You will obtain a map Map<String, Optional<Product>>, now can convert it to a List in this way :
yourObtainedMap.entrySet()
.stream().map(entry -> entry.getValue().get())
.collect(Collectors.toList())
You can't remove elements from a list while you're sorting it.
I would do this as a comparator:
new Comparator<Products>() {
#Override
public int compare(Products ob1, Products ob2) {
if (ob1.getCode().equals(ob2.getCode())) {
if (ob1.getQuantity()<ob2.getQuantity()){
return -1;
} else if (ob1.getQuantity()<ob2.getQuantity()){
return 1;
} else {
return 0;
}
}
return 0;
}
}
You can then remove the duplicates:
String prevCode = "";
for (int i = mProductsToBeSent.size(); --i >= 0) {
Products prod = mProductsToBeSent.get(i);
if (prod.getCode().equals(prevCode)) {
mProductsToBeSent.remove(i);
} else {
prevCode = prod.getCode();
}
}
UPDATE:
But if you keep your list sorted by product code, you can remove duplicates whenever you're adding to the quantity:
private static final Comparator<Products> PRODUCTS_CODE_COMPARATOR = new Comparator<Products>() {
#Override
public int compare(Products ob1, Products ob2) {
return ob1.getCode().compareTo(ob2.getCode());
}
};
To add a Products object:
int pos = Collections.binarySearch(mProductsToBeSent, obj, PRODUCTS_CODE_COMPARATOR);
if (pos >= 0) {
// already in the list
Products obj2 = mProductsToBeSent.get(pos);
obj2.setQuantity(obj2.getQuantity() + obj.getQuantity());
} else {
// not found: insert obj
mProductsToBeSent.add(-pos-1, obj);
}
I want to create a table like structure in Java as shown in the image
Table structure
Though I am not an expert in Java, I have tried to implement it using Arraylist structure as follows:
List<List<Double>> dataList = new ArrayList<List<Double>>();
for(int x = 0; x < n; x++){
List<Double> tempList = new ArrayList<Double>();
dataList.add(tempList);
}
for(int y = 0; y < n; y++){
double execution = exectime[y];
double cost= cost[y];
dataList.get(y).add(execution);
dataList.get(y).add(cost);
}
for (int z=0;z<dataList.size();z++) {
Double v1=dataList.get(z).get(0);
Double v2=dataList.get(z).get(1);
System.out.println("ID"+z +" Execution time:" + v1 + "cost: " + v2);
}
Where the values of 'n', 'exectime[n]' and 'cost[n]' will be read from a file and 'n' is the total number of 'ids' that needs to be created.
After creating the table, I want to sort it based on the 'execution time' value and 'cost' value, both increasing and decreasing order. Please help me in this regards.
#snovelli's answer about using a class to encapsulate your data is a good point.
If you are using Java 8, you can easily create and chain comparators that use accessors.
For sorting a list of objects, it might look something like:
List<ExecutionTimeData> dataList = new ArrayList<>();
dataList.sort(Comparator
.comparing(ExecutionTimeData::getExecutionTime)
.thenComparing(ExecutionTimeData::getCost));
Sorting by execution time, followed by cost.
You could also use this to sort a List<List<Double>> if you really wanted to.
List<List<Double>> doubleListList = new ArrayList<>();
doubleListList.sort(Comparator
.comparing((List<Double> l) -> l.get(0))
.thenComparing(l -> l.get(1)));
Sorting by element 0 of the list, followed by element 1.
Or for sorting in reverse order:
List<ExecutionTimeData> dataList = new ArrayList<>();
dataList.sort(Comparator
.comparing(ExecutionTimeData::getExecutionTime).reversed()
.thenComparing(ExecutionTimeData::getCost).reversed());
Use Collections.sort() with Comparator.
However, you will loss your ID information because it is based on your index of the ArrayList. Therefore, if you use this method and want to keep you ID information, you need to add() ID to your ArrayList just like execution and cost.
Comparator<List<Double>> ORDER = new Comparator<List<Double>>() {
#Override
public int compare(List<Double> lhs, List<Double> rhs) {
if (lhs.get(1) < rhs.get(1)) return -1;
if (lhs.get(1) == rhs.get(1)) return 0;
return 1;
}
};
Collections.sort(dataList, ORDER);
In above code, your dataList will sorted with cost, because it is at the index 1 of the ArrayList.
However, the better way (in readability) is you put your column into a Class, not just a ArrayList. For example, you can create a Class like this:
class Information {
private int id;
private double execution;
private double cost;
Information(int id, double execution, double cost) {
this.id = id;
this.execution = execution;
this.cost = cost;
}
}
And implement static Comparator inside that class. It will improve the readability of your code.
I think You should use a Chained Comparator to implement sorting using multiple attributes. Because If you use a single Comparator Individually It will sort the data according to its own Compare() Method Implementation.
Better to Go with Chained Comparator which sort your data on multiple attribute ... Try the Following Link ==> Sorting a list by multiple attributes example
Use Collections as List < RegisterType > , RegisterType is created according to the type of registers present in the table (ex: with 3 double atributes)
Implement the Comparator interface Comparator< RegisterType >
Override the compare( RegisterType o1, RegisterType o2) method the way you want (define how to sort 2 elements of type RegisterType)
Inkove Collections.sort(List< RegisterType > list, ComparatorClass)
Then you will have your collection list sorted the way you want.
A table is a way to represent a list of objects, why not use a list of object then?
I think you want to have a SortedSet of a class that you could define as:
public class ExecutionTimeData{
private final long id;
private final long executionTime;
private final int cost;
public ExecutionTimeData(long id, long executionTime, int cost){
this.id = id;
this.executionTime = executionTime;
this.cost = cost;
}
/* Getters */
}
Then you will simply have an unsorted list like
List<ExecutionTimeData> unsortedList = new ArrayList<>();
As pointed out from #VikrantKashyap to order the list with both value and cost you then must implement a Chained Comparator
public class ExecutionTimeDataChainedComparator implements Comparator<ExecutionTimeData> {
private List<Comparator<ExecutionTimeData>> listComparators;
#SafeVarargs
public ExecutionTimeDataChainedComparator (Comparator<ExecutionTimeData>... comparators) {
this.listComparators = Arrays.asList(comparators);
}
#Override
public int compare(ExecutionTimeData etd1, ExecutionTimeData etd2) {
for (Comparator<ExecutionTimeData> comparator : listComparators) {
int result = comparator.compare(etd1, etd2);
if (result != 0) {
return result;
}
}
return 0;
}
}
And implement the comparators like this
public class ExecutionTimeDataCostComparator implements Comparator<ExecutionTimeData > {
#Override
public int compare(ExecutionTimeData a, ExecutionTimeData b) {
return b.getCost() > a.getCost()?-1:1;
}
}
public class ExecutionTimeDataExecutionComparator implements Comparator<ExecutionTimeData > {
#Override
public int compare(ExecutionTimeData a, ExecutionTimeData b) {
return b.getExecutionTime() > a.getExecutionTime()?-1:1;
}
}
And of course you can find out an easy way to invert the order by instantiating the comparators providing ASCENDING or DESCENDING order
Consider the following table:
Name Code Number
Mike x6 5.0
Mike b4 3.0
Mike y2 1.0
Tom y2 4.5
Tom x6 4.5
Tom b4 1.0
Susi x6 4.0
Susi y2 3.0
Susi b4 2.0
I have three columns, it should be sorted first of all by the column "Name" and then by the column "Number". I wanted to do this with Dictionary (use String array as value and Double as key) and then sort by value, but I miss the sort by the name.
Map<Double, String[]> map = new HashMap<Double, String[]>();
map.put(5.0, {"Mike", "x6"});
System.out.println(map.get(5.0));
I don't know what is the best way to store my data. I would like also to know the solution in Java 8.
First of all, you should make each line of your table an object:
public class MyData {
private String name;
private String code;
private Double number;
public MyData(String name, String code, Double number) {
this.name = name;
this.code = code;
this.number = number;
}
public String getName() {
return name;
}
public String getCode() {
return code;
}
public Double getNumber() {
return number;
}
}
Using Map<Double, String[]> does not represent what you are trying to achieve. A Map is used to create a link between an unique key an a value. Does it make sense for each number to be associated to a name and a code?
Once you have this object, it is much easier to sort it according to its properties:
List<MyData> list = new ArrayList<>();
list.add(new MyData("Mike", "x6", 5.0));
list.add(new MyData("Mike", "b4 ", 3.0));
list.add(new MyData("Mike", "y2", 1.0));
list.add(new MyData("Tom", "y2", 4.5));
List<MyData> sortedList = list.stream()
.sorted(Comparator.comparing(MyData::getName).thenComparing(MyData::getNumber))
.collect(Collectors.toList());
I think a Map is the wrong data structure for your case, as Maps explicitly DO NOT DEFINE an order based on the values.
But you may help yourself with streams. Something like:
map.entrySet().stream().sorted((e1, e2) -> e1.getValue()[0].compareTo(e2.getValue()[0])).map(e -> e.getKey()).toArray(l -> new Integer[l])
this will give you an array of keys, sorted by the first integer in the value array. the full value you may then look up in the original map.
I am trying to get the shorest distance and its stop_lat, stop_lon between the incoming latD, longD and the stored one in the stops table. I am storing the lat_stop, lon_stop, distStops in double tow dimensional arrayList. Currently I am getting this error
The method min(Collection, Comparator) in the type Collections is not applicable for the arguments
(List>, new Comparator>(){})
The method sort(List, Comparator) in the type Collections is not applicable for the arguments
(ArrayList>, new Comparator>(){})
Example:
(140.4, 83.346723, 12.567835),
(90.6, 83.0984543, 10.347291),
(6.4, 83.6453974, 12.570937),
(25.7, 83.198472, 13.7364563)
I want to get this set (6.4, 83.6453974, 12.570937)
How can I get the shortest distance and its related stop_lat, stop_lon?
I appreciate any help.
// the stops and arrrivaltimes tables exist.
PreparedStatement preparedLatLong = con
.prepareStatement("SELECT lat, longi, name from stops");
ResultSet rsLatLong = preparedLatLong.executeQuery();
// ArrayList<Double> distanceHistory = new ArrayList<Double>();
ArrayList<List<Double>> distanceHistory = new ArrayList<List<Double>>();
while (rsLatLong.next()) {
double lat_stop = rsLatLong.getDouble("lat");
double lon_stop = rsLatLong.getDouble("longi");
double distStops = haversineDistance(latD, longD, lat_stop,
lon_stop);
distanceHistory.add(Arrays.asList(distStops, lat_stop,
lon_stop));
;
}
//Find the shortest diestance and its related longi and lati
Collections.sort(distanceHistory,
new Comparator<ArrayList<Double>>() {
#Override
public int compare(ArrayList<Double> o1,
ArrayList<Double> o2) {
// TODO Auto-generated method stub
return o1.get(0).compareTo(o2.get(0));
}
}
);
You have defined your distanceHistory list as ArrayList<List<Double>>. This means that each element in this list is a List<Double>.
But then, you defined your comparator as a Comparator<ArrayList<Double>>. This means that it expects the items it compares to be specifically ArrayList<Double>.
When you use Collections.sort, it expects a comparator whose base type is more general than the base type of the collection. And ArrayList<Double> is not more general than List<Double>.
The simple solution is to change the definition of the comparator to Comparator<List<Double>>.
But this design is really not very good. You are supposed to use lists for "similar" things. A list of three doubles that do not represent the same sort of information is not a good design. It would be better to create a small class for this:
private static class StopItem implements Comparable<StopItem> {
double stopLat, stopLon, stopDist;
public StopItem( double stopLat, stopLon, stopDist ) {
this.stopLat = stopLat;
this.stopLon = stopLon;
this.stopDist = stopDist;
}
// Getters, setters...
#Override
public int compareTo( StopItem otherItem ) {
return Double.compare( this.stopDist, otherItem.stopDist );
}
}
You can then create a list of these objects, and use Collections.sort() on it, and you don't need an extra comparator.
For example, here is how you'd fill your list:
List<StopItem> distanceHistory = new ArrayList<>();
while (rsLatLong.next()) {
double latStop = rsLatLong.getDouble("lat");
double lonStop = rsLatLong.getDouble("longi");
double distStop = haversineDistance(latD, longD, latStop, lonStop);
StopItem newItem = new StopItem( latStop, lonStop, distStop );
distanceHistory.add(newItem);
}
And then you can use Collections.sort(distanceHistory).
First of all, the Comparator should take two arguments of type List<Double>:
Collections.sort( distanceHistory,
new Comparator<List<Double>>()
{
#Override
public int compare(List<Double> o1,List<Double> o2 ) {
...
since that's type of the elements of
ArrayList<List<Double>> distanceHistory = new ArrayList<List<Double>>();
(Does your code even compile as it is?)
Secondly you might want to work on your data structure a bit; perhaps a tuple/class with three attributes rather than just a List - right now you're in object denial :-) Eg.
class MyThingy implements Comparable<MyThingy> {
Double lat, lon, dist;
#Override compareTo( MyThingy other ) {
// implement sensibly.
}
}
Then you can just
List<MyThingy> distanceHistory = new ArrayList<MyThingy>();
...
Collections.sort( distanceHistory );
without having to supply an anonymous comparator.
Cheers,