I have the following class Person -
Person.java -
public class Person {
private int id;
private String department;
private double salary;
public Person(int id, String department, double salary) {
this.id = id;
this.department = department;
this.salary = salary;
}
public String getDepartment() {
return department;
}
public double getSalary() {
return salary;
}
#Override
public String toString() {
return "Person{" +
"id=" + id +
", department='" + department + '\'' +
", salary=" + salary +
'}';
}
}
It has the fields -
id, department, salary
Now I have first predicate -
Predicate<List<Person>> hasSalaryOf40k = list -> {
boolean myReturn = false;
Iterator<Person> iterator = list.iterator();
while (iterator.hasNext()) {
Person person = iterator.next();
double salary = person.getSalary();
if (salary == 40000) {
myReturn = true;
break;
}
}
return myReturn;
};
Here, I want to filter out those lists having persons with salary as 40K.
Second predicate -
Predicate<List<Person>> isDeveloper = list -> {
boolean myReturn = false;
Iterator<Person> iterator = list.iterator();
while (iterator.hasNext()) {
Person person = iterator.next();
String department = person.getDepartment();
if (department.equals("Developer")) {
myReturn = true;
break;
}
}
return myReturn;
};
Here, I want to filter out those lists having persons with department as 'developer'
Third predicate -
Predicate<List<Person>> hasSalaryOf40kAndIsDeveloper = list ->
hasSalaryOf40k.and(isDeveloper).test(list);
Here, I want to filter out those lists having persons with both salary as 40K and department as "developer"
Now I have the following two lists -
List<Person> list1 = new ArrayList<>(List.of(
new Person(1, "Developer", 35000),
new Person(2, "Accountant", 40000),
new Person(3, "Clerk", 20000),
new Person(4, "Manager", 50000)
));
List<Person> list2 = new ArrayList<>(List.of(
new Person(1, "Developer", 40000),
new Person(2, "Accountant", 35000),
new Person(3, "Clerk", 22000),
new Person(4, "Manager", 55000)
));
The list1 does not match the desired criteria while list2 matches the desired criteria.
Now I call the predicate method test -
System.out.println(hasSalaryOf40kAndIsDeveloper.test(list1));
System.out.println(hasSalaryOf40kAndIsDeveloper.test(list2));
Output -
true
true
Desired output -
false
true
Where am I going wrong and how to correct my code?
You're applying the predicate to the whole list and not each element of the list, so it's true that the list contains a developer and its true that the list contains a salary over 40k. You need to apply the predicate to the Person object rather than the List<Person> object
Related
I am solving a problem. I have to create a TreeSet of custom Employee objects where data should be sorted by salary but employee Id needs to be unique. I understand that equals() and hashCode() method doesn't work for TreeSet and we need to write our object are equal or not logic inside the compareTo() method. I am checking if both employee Id are equal then return 0, means object should not be added.
But the output is not coming as desired as employees with same employee Id are also getting added. I tried to debug this but I am not getting the right answer.
This is the code.
public class Employee implements Comparable<Employee>{
int empId;
String empName;
double salary;
public Employee() {
super();
}
public Employee(int empId, String empName, double salary) {
super();
this.empId = empId;
this.empName = empName;
this.salary = salary;
}
#Override
public int hashCode() {
return empId;
}
#Override
public boolean equals(Object o) {
if(this == o) return true;
if(o == null || this.getClass() != o.getClass()) return false;
Employee e = (Employee) o;
return (this.empId == e.empId);
}
#Override
public String toString() {
return empId + " " + empName + " " + salary;
}
#Override
public int compareTo(Employee e) {
if(empId == e.empId)
return 0;
if(this.salary < e.salary) {
return -1;
}
else {
return 1;
}
}
}
The main method of program
public static void main(String[] args) {
TreeSet<Employee> eSet = new TreeSet<>();
eSet.add(new Employee(1, "john", 20000));
eSet.add(new Employee(2, "jim", 10000));
eSet.add(new Employee(9, "mike", 50000));
eSet.add(new Employee(3, "jack", 30000));
eSet.add(new Employee(3, "david", 40000));
eSet.add(new Employee(9, "liam", 80000));
eSet.add(new Employee(9, "brad", 89000));
eSet.add(new Employee(3, "jason", 85000));
eSet.add(new Employee(2, "ted", 35000));
for(Employee e: eSet) {
System.out.println(e);
}
}
The output of above program comes out as
2 jim 10000.0
1 john 20000.0
3 jack 30000.0
2 ted 35000.0
9 mike 50000.0
3 jason 85000.0
Here as you can see employees with same employee Id are being added to the TreeSet which should not happen. If I am using a HashSet the problem is resolved, but I have to implement it using TreeSet to get the sorted behaviour.
Can someone please guide me where am I going wrong?
The implementation of Comparable violates the contract of Comparable::compareTo, in particular this part:
Finally, the implementor must ensure that x.compareTo(y)==0 implies that signum(x.compareTo(z)) == signum(y.compareTo(z)), for all z.
We can demonstrate this violation with the following code:
final Employee jim = new Employee(2, "jim", 10_000);
final Employee ted = new Employee(2, "ted", 35_000);
final Employee john = new Employee(9, "john", 20_000);
System.out.println("jim compare to ted: " + jim.compareTo(ted));
System.out.println("john compare to jim: " + john.compareTo(jim));
System.out.println("john compare to ted: " + john.compareTo(ted));
leading to the following output:
jim compare to ted: 0
john compare to jim: 1
john compare to ted: -1
Ideone demo
We can fix this issue by dropping the salary from the compareTo-method and only order by empId:
#Override
public int compareTo(Employee e) {
return Integer.compare(empId, e.empId);
}
Ideone demo
I have 2 Lists:
// old list
List<Employee> oldList = new ArrayList<>();
Employee emp1 = new Employee();
emp1.setPersonalNumber("123");
emp1.setName("old_name1");
emp1.setStatus(Status.OLD);
Employee emp2 = new Employee();
emp2.setPersonalNumber("456");
emp2.setName("old_name2");
emp2.setStatus(Status.OLD);
oldList.add(emp1);
oldList.add(emp2);
// new list
List<Employee> newList = new ArrayList<>();
Employee newEmp1 = new Employee();
newEmp1.setPersonalNumber("123");
newEmp1.setName("new_name1");
newEmp1.setStatus(Status.NEW);
Employee newEmp2 = new Employee();
newEmp2.setPersonalNumber("456");
newEmp2.setName("new_name2");
newEmp2.setStatus(Status.NEW);
newList.add(newEmp1);
newList.add(newEmp2);
Does anyone know how can I merge those 2 Lists to one List containing all the employees from both lists grouped by "PersonalNumber" and keeping the order of the elemets in newList?
newList comes from the Database with a predefined sorting, and I need to keep it that way, so I can't sort it again on the Java side
Result should be:
[
{"123", "new_name1", NEW},
{"123", "old_name1", OLD},
{"456", "new_name2", NEW},
{"456", "old_name1", OLD},
]
I have the guarantee that both lists have the same size and contain employees with the same personalNumbers. I just want to "inject" each old employee under the new employee with the same personalNumber
You can do like this: As you mentioned that both lists have the same PersonalNumber so you can group by using this property. To ensure order based on the personalNumber, I've used LinkedHashMap.
Stream.concat(newList.stream(), oldList.stream())
.collect(Collectors.groupingBy(Employee::getPersonalNumber,
LinkedHashMap::new, Collectors.toList()))
.values().stream().flatMap(List::stream)
.collect(Collectors.toList());
Note: The result of stream#concat is ordered if both of the input streams are ordered.
Collections to sort should work for this.
newList.addAll(oldList);
Collections.sort(newList, Comparator.comparing(Employee::getPersonalNumber) );
The key is that, "This sort is guaranteed to be stable: equal elements will not be reordered as a result of the sort."
Since cannot sort the new list, I take that to mean you don't know the order of the new list. You can do it the ol' N^2 method.
for(int i = 0; i<newList.size(); i+=2){
String newNum = newList.get(i).getPersonalNumber();
Employee old = oldList.stream().filter(
emp->newNum.equals(
emp.getPersonalNumber()
)
).findFirst().orElse(null);
newList.add(i+1, old);
oldList.remove(old); //not nescessary?
}
Here is another approach with Java streams and InsStream
private List<Employee> mergeLists(List<Employee> oldList, List<Employee> newList) {
return IntStream.range(0, oldList.size())
.mapToObj(index -> Arrays.asList(newList.get(index), oldList.get(index)))
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
This approach is possible because of
I have the guarantee that both lists have the same size and contain employees with the same personalNumbers.
You can do it as follows:
// Sort oldList on personalNumber for faster access when retrieving the records
// with same personalNumber
oldList.sort(Comparator.comparing(Employee::getPersonalNumber));
List<Employee> result = new ArrayList<Employee>();
for (int i = 0; i < newList.size(); i++) {
// Get an employee, `e` from `newList` and add to `result`
Employee e = newList.get(i);
result.add(e);
// Add elements from `newList` to `result` until a different `personalNumber`
// occurs
while (i < newList.size() - 1 && e.getPersonalNumber().equals(newList.get(i + 1).getPersonalNumber())) {
result.add(newList.get(++i));
}
// Iterate `oldList` to find an employee with the `personalNumber` equal to that
// of `e`
int j;
boolean found = false;
for (j = 0; j < oldList.size(); j++) {
if (oldList.get(j).getPersonalNumber().equals(e.getPersonalNumber())) {
found = true;
break;
}
}
// If `oldList` has an employee with the `personalNumber` equal to that of `e` ,
// add elements from `oldList` to `result` until a different `personalNumber`
// occurs. Note that `oldList` has already been sorted.
if (found) {
result.add(oldList.get(j));
while (j < oldList.size() - 1 && oldList.get(++j).getPersonalNumber().equals(e.getPersonalNumber())) {
result.add(oldList.get(j));
}
}
}
Demo:
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
enum Status {
OLD, NEW;
}
class Employee {
private String name;
private String personalNumber;
private Status status;
public Employee() {
super();
}
public Employee(String name, String personalNumber, Status status) {
this.name = name;
this.personalNumber = personalNumber;
this.status = status;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPersonalNumber() {
return personalNumber;
}
public void setPersonalNumber(String personalNumber) {
this.personalNumber = personalNumber;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
#Override
public String toString() {
return personalNumber + ", " + name + ", " + status;
}
}
public class Main {
public static void main(String[] args) {
// old list
List<Employee> oldList = new ArrayList<Employee>();
Employee emp1 = new Employee();
emp1.setPersonalNumber("123");
emp1.setName("old_name1");
emp1.setStatus(Status.OLD);
Employee emp2 = new Employee();
emp2.setPersonalNumber("456");
emp2.setName("old_name2");
emp2.setStatus(Status.OLD);
oldList.add(emp1);
oldList.add(emp2);
// new list
List<Employee> newList = new ArrayList<>();
Employee newEmp1 = new Employee();
newEmp1.setPersonalNumber("123");
newEmp1.setName("new_name1");
newEmp1.setStatus(Status.NEW);
Employee newEmp2 = new Employee();
newEmp2.setPersonalNumber("456");
newEmp2.setName("new_name2");
newEmp2.setStatus(Status.NEW);
newList.add(newEmp1);
newList.add(newEmp2);
// Sort oldList on personalNumber for faster access when retrieving the records
// with same personalNumber
oldList.sort(Comparator.comparing(Employee::getPersonalNumber));
List<Employee> result = new ArrayList<Employee>();
for (int i = 0; i < newList.size(); i++) {
// Get an employee, `e` from `newList` and add to `result`
Employee e = newList.get(i);
result.add(e);
// Add elements from `newList` to `result` until a different `personalNumber`
// occurs
while (i < newList.size() - 1 && e.getPersonalNumber().equals(newList.get(i + 1).getPersonalNumber())) {
result.add(newList.get(++i));
}
// Iterate `oldList` to find an employee with the `personalNumber` equal to that
// of `e`
int j;
boolean found = false;
for (j = 0; j < oldList.size(); j++) {
if (oldList.get(j).getPersonalNumber().equals(e.getPersonalNumber())) {
found = true;
break;
}
}
// If `oldList` has an employee with the `personalNumber` equal to that of `e` ,
// add elements from `oldList` to `result` until a different `personalNumber`
// occurs. Note that `oldList` has already been sorted.
if (found) {
result.add(oldList.get(j));
while (j < oldList.size() - 1 && oldList.get(++j).getPersonalNumber().equals(e.getPersonalNumber())) {
result.add(oldList.get(j));
}
}
}
// Display the result
result.forEach(System.out::println);
}
}
Output:
123, new_name1, NEW
123, old_name1, OLD
456, new_name2, NEW
456, old_name2, OLD
I have a list of Person object. All person has a unique id, but the person's name can be the same.
Person {
String id,
String name,
}
I want to convert this array of persons into ImmutableMap<String, ImmutableSet<String>>. The key of the map should be the user's name, the immutable set contains the ids of specific user's name.
I know how to do it using HashMap and HashSet:
for (person : personList) {
String id = person.id;
String name = person.name;
if (!hashMap.containsKey(name)) {
hashMap.put(name, new HashSet<String>());
}
hashMap.get(name).add(id);
}
I want to know how to do it using ImmutableMap, ImmutableSet with lambda.
Here is one possible solution.
First, generate some data. Three names and 18 ids. Put them in a list.
Random r = new Random();
int[] ids = r.ints(1000, 1, 1000).distinct().limit(18).toArray();
int id = 0;
List<Person> people = new ArrayList<>();
for (int i = 0; i < 6; i++) {
for (String name : List.of("Bob", "Joe", "Mary")) {
people.add(new Person(name, ids[id++]));
}
}
Now creating the map.
Use groupingBy to create a key pointing to a collection. The key is the
name and the collection is the map.
The collection(a set) holds the ids
Map<String, Set<Integer>> nameToID =
Collections.unmodifiableMap(people.stream().collect(
Collectors.groupingBy(Person::getName, Collectors.mapping(
Person::getID, Collectors.toUnmodifiableSet()))));
Print them.
nameToID.entrySet().forEach(
e -> System.out.println(e.getKey() + " -> " + e.getValue()));
}
}
Here is the Person class with some additional methods and a constructor.
class Person {
String name;
int id;
public Person(String name, int id) {
this.name = name;
this.id = id;
}
public String getName() {
return name;
}
public int getID() {
return id;
}
public String toString() {
return "(" + name + "," + id + ")";
}
}
This question already has answers here:
Sort ArrayList of custom Objects by property
(29 answers)
Closed 6 years ago.
My code is as follows:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Sorting {
public static void main(String[] args) {
// TODO Auto-generated method stub
List<String> theMenu = new ArrayList<String>();
String[] Array1 = {
"M1", "Wine", "2.50",
"M2", "Soft drink", "1.50",
"D1", "Fish", "7.95",
"D2", "Veg chili", "6.70"
};
theMenu.addAll(Arrays.asList(Array1));
String[] temp1 = new String[3];
String[] temp2 = new String[3];
for (int i = 0; i < theMenu.size(); i+=3) {
for (int j = i + 3; j < theMenu.size(); j+=3) {
if (i < theMenu.size() - 3) {
if (theMenu.get(i).compareTo(theMenu.get(i + 3)) > 0) {
temp1[0] = theMenu.get(i);
temp1[1] = theMenu.get(i + 1);
temp1[2] = theMenu.get(i + 2);
temp2[0] = theMenu.get(j);
temp2[1] = theMenu.get(j+1);
temp2[2] = theMenu.get(j+2);
theMenu.remove(j + 2);
theMenu.remove(j + 1);
theMenu.remove(j);
theMenu.remove(i + 2);
theMenu.remove(i + 1);
theMenu.remove(i);
theMenu.add(i, temp2[0]);
theMenu.add(i + 1, temp2[1]);
theMenu.add(i + 2, temp2[2]);
theMenu.add(j, temp1[0]);
theMenu.add(j + 1, temp1[1]);
theMenu.add(j + 2, temp1[2]);
}
}
}
}
System.out.println(theMenu);
}
}
I want to sort the ArrayList in the order D1, D2, M1, M2, M3, while keeping its respective items and price WITH the IDs. I am not allowed to change the storing method i.e make another class of Items with its own ID and name and price. How can I rearrange it so that it is in the form :
{"D1" , "Fish", "7.95"
"D2" , "Veg chili", "6.70",
"M1" , "Wine", "2.50",
"M2", "Soft drink", "1.50"
}
Inside the ArrayList. This should work regardless of how many items we store inn the arrayList. My code produces the following output:
[M1, Wine, 2.50, M2, Soft drink, 1.50, D1, Fish, 7.95, D2, Veg chili, 6.70]
Note: Forget the new lines in the array, I just need the indexes sorted out. Can anyone help me with this?
Firstly, you have a entity - product, which has Id, name and price.
Always create new class for each Entity in your application.
Example:
public class MyObject implements Comparable
{
private String id;
private String name;
private double price;
public MyObject(String id, String name, double price)
{
this.id = id;
this.name = name;
this.price = price;
}
public String getId()
{
return id;
}
#Override
public int compareTo(Object o)
{
MyObject receivedObject = (MyObject) o;
return this.id.compareTo(receivedObject.getId());
}
#Override
public String toString()
{
return "MyObject{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", price=" + price +
'}';
}
}
I use "implements Comparable" for easy write sorting. When we implement this interphace we must override
public int compareTo(Object o)
{
MyObject receivedObject = (MyObject) o;
return this.id.compareTo(receivedObject.getId());
}
This method compare 2 Objects and say which object "bigger". In your case we need compare only by Ids.
Now you have entity with ability to compare with each other. Check it:
public class Processing
{
public static void main(String[] argc) {
List<MyObject> list = new ArrayList<>();
list.add(new MyObject("M1", "Wine", 2.50));
list.add(new MyObject("M2", "Soft drink", 1.50));
list.add(new MyObject("D1", "Fish", 7.95));
list.add(new MyObject("D2", "Veg chili", 6.70));
System.out.println(list);
Collections.sort(list);
System.out.println(list);
}
}
First output:
MyObject{id='M1', name='Wine', price=2.5},
MyObject{id='M2', name='Soft drink', price=1.5},
MyObject{id='D1', name='Fish', price=7.95},
MyObject{id='D2', name='Veg chili', price=6.7}
Second output:
MyObject{id='D1', name='Fish', price=7.95},
MyObject{id='D2', name='Veg chili', price=6.7},
MyObject{id='M1', name='Wine', price=2.5},
MyObject{id='M2', name='Soft drink', price=1.5}
I created an ArrayList of Array type like below,
ArrayList<Object[]> csvArray = new ArrayList<Object[]>();
As you can see, each element of the ArrayList is an array like {Country, City, Name, Age}.
Now I'm wanting to do a "group by" on Country and City (combined), followed by taking the average Age of the people for each Country+City.
May I know what is the easiest way to achieve this? Or you guys have suggestions to use data structures better than ArrayList for this "group by" and aggregation requirements?
Your answers are much appreciated.
You will get lot of options in Java 8.
Example
Stream<Person> people = Stream.of(new Person("Paul", 24), new Person("Mark",30), new Person("Will", 28));
Map<Integer, List<String>> peopleByAge = people
.collect(groupingBy(p -> p.age, mapping((Person p) -> p.name, toList())));
System.out.println(peopleByAge);
If you can use Java 8 and no specific reason for using a data structure, you can go through below tutorial
http://java.dzone.com/articles/java-8-group-collections
You could use Java 8 streams for this and Collectors.groupingBy. For example:
final List<Object[]> data = new ArrayList<>();
data.add(new Object[]{"NL", "Rotterdam", "Kees", 38});
data.add(new Object[]{"NL", "Rotterdam", "Peter", 54});
data.add(new Object[]{"NL", "Amsterdam", "Suzanne", 51});
data.add(new Object[]{"NL", "Rotterdam", "Tom", 17});
final Map<String, List<Object[]>> map = data.stream().collect(
Collectors.groupingBy(row -> row[0].toString() + ":" + row[1].toString()));
for (final Map.Entry<String, List<Object[]>> entry : map.entrySet()) {
final double average = entry.getValue().stream()
.mapToInt(row -> (int) row[3]).average().getAsDouble();
System.out.println("Average age for " + entry.getKey() + " is " + average);
}
You can check the collections recommended by #duffy356. I can give you an standard solution related with java.utils
I'd use a common Map<Key,Value> and being specific a HashMap.
For the keys, as I can see, you'll need and extra plain object which relates country and city. The point is create a working equals(Object) : boolean method. I'd use the Eclipse-auto generator; for me it gives me the following:
class CountryCityKey {
// package visibility
String country;
String city;
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((country == null) ? 0 : country.hashCode());
result = prime * result + ((region == null) ? 0 : region.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CountryCityKey other = (CountryCityKey) obj;
if (country == null) {
if (other.country != null)
return false;
} else if (!country.equals(other.country))
return false;
if (region == null) {
if (other.region != null)
return false;
} else if (!region.equals(other.region))
return false;
return true;
}
}
Now we can group or objects in a HashMap<CountryCityKey, MySuperObject>
The code for that could be:
Map<CountryCityKey, List<MySuperObject>> group(List<MySu0perObject> list) {
Map<CountryCityKey, MySuperObject> response = new HashMap<>(list.size());
for (MySuperObject o : list) {
CountryCityKey key = o.getKey(); // I consider this done, so simply
List<MySuperObject> l;
if (response.containsKey(key)) {
l = response.get(key);
} else {
l = new ArrayList<MySuperObject>();
}
l.add(o);
response.put(key, l);
}
return response;
}
And you have it :)
you could use the brownies-collections library of magicwerk.org (http://www.magicwerk.org/page-collections-overview.html)
they offer keylists, which fit your requirements.(http://www.magicwerk.org/page-collections-examples.html)
I would recommend an additional step. You gather your data from CSV in Object[]. If you wrap your data into a class containing these data java8 collections will easily help you. (also without but it is more readable and understandable)
Here is an example - it introduces a class Information which contains your given data (country, city,name, age). The class has a constructor initializing these fields by a given Object[] array which might help you to do so - BUT: the fields have to be fixed (which is usual for CSV):
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class CSVExample {
public static void main(String[] args) {
ArrayList<Information> csvArray = new ArrayList<>();
csvArray.add(new Information(new Object[] {"France", "Paris", "Pierre", 34}));
csvArray.add(new Information(new Object[] {"France", "Paris", "Madeleine", 26}));
csvArray.add(new Information(new Object[] {"France", "Toulouse", "Sam", 34}));
csvArray.add(new Information(new Object[] {"Italy", "Rom", "Paul", 44}));
// combining country and city with whitespace delimiter to use it as the map key
Map<String, List<Information>> collect = csvArray.stream().collect(Collectors.groupingBy(s -> (s.getCountry() + " " + s.getCity())));
//for each key (country and city) print the key and the average age
collect.forEach((k, v) -> System.out.println(k + " " + v.stream().collect(Collectors.averagingInt(Information::getAge))));
}
}
class Information {
private String country;
private String city;
private String name;
private int age;
public Information(Object[] information) {
this.country = (String) information[0];
this.city = (String) information[1];
this.name = (String) information[2];
this.age = (Integer) information[3];
}
public Information(String country, String city, String name, int age) {
super();
this.country = country;
this.city = city;
this.name = name;
this.age = age;
}
public String getCountry() {
return country;
}
public String getCity() {
return city;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
#Override
public String toString() {
return "Information [country=" + country + ", city=" + city + ", name=" + name + ", age=" + age + "]";
}
}
The main shows a simple output for your question.
In java 8 the idea of grouping objects in a collection based on the values of one or more of their properties is simplified by using a Collector.
First, I suggest you add a new class as follow
class Info {
private String country;
private String city;
private String name;
private int age;
public Info(String country,String city,String name,int age){
this.country=country;
this.city=city;
this.name=name;
this.age=age;
}
public String toString() {
return "("+country+","+city+","+name+","+age+")";
}
// getters and setters
}
Setting up infos
ArrayList<Info> infos =new ArrayList();
infos.add(new Info("USA", "Florida", "John", 26));
infos.add(new Info("USA", "Florida", "James", 18));
infos.add(new Info("USA", "California", "Alan", 30));
Group by Country+City:
Map<String, Map<String, List<Info>>>
groupByCountryAndCity = infos.
stream().
collect(
Collectors.
groupingBy(
Info::getCountry,
Collectors.
groupingBy(
Info::getCity
)
)
);
System.out.println(groupByCountryAndCity.get("USA").get("California"));
Output
[(USA,California,James,18), (USA,California,Alan,30)]
The average Age of the people for each Country+City:
Map<String, Map<String, Double>>
averageAgeByCountryAndCity = infos.
stream().
collect(
Collectors.
groupingBy(
Info::getCountry,
Collectors.
groupingBy(
Info::getCity,
Collectors.averagingDouble(Info::getAge)
)
)
);
System.out.println(averageAgeByCountryAndCity.get("USA").get("Florida"));
Output:
22.0
/* category , list of cars*/
Please use the below code : I have pasted it from my sample app !Happy Coding .
Map<String, List<JmCarDistance>> map = new HashMap<String, List<JmCarDistance>>();
for (JmCarDistance jmCarDistance : carDistanceArrayList) {
String key = jmCarDistance.cartype;
if(map.containsKey(key)){
List<JmCarDistance> list = map.get(key);
list.add(jmCarDistance);
}else{
List<JmCarDistance> list = new ArrayList<JmCarDistance>();
list.add(jmCarDistance);
map.put(key, list);
}
}
Best data structure is a Map<Tuple, List>.
Tuple is the key, i.e. your group by columns.
List is used to store the row data.
Once you have your data in this structure, you can iterate through each key, and perform the aggregation on the subset of data.