Get duplicated items with specific format from Java Stream - java

I'm new with Java streams and I'm playing around with them right now. Given that I receive a list of persons I want to detect which of them are duplicated and print them as "{Id1} is duplicated with {Id3}{Id4} and its duplicated values are Name, Lastname, FamilyName and Birthday"
So this is my person class, I have already override the equals method in order to get the duplicated based on my criterias
public class Person {
private int id;
private String name;
private String familyName;
private String birthday;
private String city;
public Person(int id, String name, String familyName, String birthday, String city) {
this.id = id;
this.name = name;
this.familyName = familyName;
this.birthday = birthday;
this.city = city;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getFamilyName() {
return familyName;
}
public void setFamilyName(String familyName) {
this.familyName = familyName;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
#Override
public int hashCode() {
return Objects.hash( name,familyName,birthday,city);
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return false;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Person other = (Person) obj;
if (!Objects.equals(name, other.name)) {
return false;
}
if (!Objects.equals(familyName, other.familyName)) {
return false;
}
if (!Objects.equals(birthday, other.birthday)) {
return false;
}
return true;
}
}
Then, I'm getting the list of duplicates in the following method
personList.stream()
.filter(p -> personList.contains(p))
.collect(Collectors.toList()).forEach(p-> {
System.out.println(p.getId() + " " + p.getName() + " " + p.getFamilyName() + " " + p.getBirthday());
});
It prints the following:
2 Andres Gonzalez 12/4/1990
4 Maureen Perez 15/07/92
7 Andres Gonzalez 12/4/1990
9 Maureen Perez 15/07/92
11 Maureen Perez 15/07/92
As you can see ID's 2 and 7 are duplicated and also 4,9 and 11 are duplicated and those ones are the ones that I need to print in that format but I don't know how to do it with streams so far.

First of all, you should fix your hashCode() implementation to match your equals. If two objects are equal, they must have the same hashCode().
Now, your Stream pipeline returns all elements, since your filter's Predicate will always return true.
Instead, you can group equal elements of the List:
Map<Person,List<Integer>> grouped =
personList.stream()
.collect(Collectors.groupingBy(Function.identity(),
Collectors.mapping(Person::getId,
Collectors.toList())));
Now, for each Person, you have an associated List of identifiers.
You can iterate this Map and print the Persons having Lists with size > 1.
For example:
personList.stream()
.collect(Collectors.groupingBy(Function.identity(),
Collectors.mapping(Person::getId,
Collectors.toList())));
.entrySet()
.stream()
.filter(e -> e.getValue().size() > 1)
.forEach(e -> System.out.println(e.getKey().getId() + " " + e.getKey().getName() + " " + e.getKey().getFamilyName() + " " + e.getKey().getBirthday() + " " + e.getValue()));

Related

How to find getter method on basis of JSON Value using Jackson

I have json similar to this :
Groups":[
{
"LogicalOperator":"AND",
"condition":[
{
"column":"name",
"Operator":"CONTAINS",
"Value":"Shiva"
},
{
"column":"address",
"Operator":"NOT CONTAINS",
"Value":"Vijay Nagar"
},
{
"column":"city",
"Operator":"EQUAL",
"Value":"Bengaluru"
},
{
"column":"country",
"Operator":"NOT EQUAL",
"Value":"India"
}
]
}
How to find getter method based on column value.
Example: Below column is having "name", "address", "city" and "country".
If the column value is "name" then dynamically I want find getName() method, if the column value is "address" then it should be getAddress()...
Below is the pojo:
public class CustomerPojo {
private String name;
private int age;
private String address;
private String city;
private String country;
public CustomerPojo(String name, String address, String city, String country,int age) {
super();
this.name = name;
this.age=age;
this.address = address;
this.city = city;
this.country = country;
}
#Override
public int hashCode() {
return Objects.hash(address, age, city, country, name);
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CustomerPojo other = (CustomerPojo) obj;
return Objects.equals(address, other.address) && age == other.age && Objects.equals(city, other.city)
&& Objects.equals(country, other.country) && Objects.equals(name, other.name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
#Override
public String toString() {
return "CustomerPojo [name=" + name + ", age=" + age + ", address=" + address + ", city=" + city + ", country="
+ country + "]";
}
}
Below is the code which I have tried. But I want to do it dynamically.
private List<CustomerPojo> groupOperatorAND(JsonNode condNode, List<CustomerPojo> list) {
// String jsonNode = condNode
// System.out.println(jsonNode);
String column = condNode.findValue("column").asText();
String operator = condNode.findValue("Operator").asText();
String value = condNode.findValue("Value").asText();
switch (operator) {
case "CONTAINS":
if (column.equals("name")) {
containsList = list.stream().filter(li -> li.getName().contains(value)).collect(Collectors.toList());
} else {
containsList = list.stream().filter(li -> li.getAddress().contains(value)).collect(Collectors.toList());
}
// System.out.println(containsList);
// objList.add(containsList);
break;
case "NOT CONTAINS":
if (column.equals("name")) {
notContainsList = containsList.stream().filter(li -> !li.getName().contains(value))
.collect(Collectors.toList());
} else {
notContainsList = containsList.stream().filter(li -> !li.getAddress().contains(value))
.collect(Collectors.toList());
}
// System.out.println(notContainsList);
// objList.add(notContainsList);
break;
case "EQUAL":
if (column.equals("name")) {
equalList = notContainsList.stream().filter(li -> li.getName().equals(value))
.collect(Collectors.toList());
} else {
equalList = notContainsList.stream().filter(li -> li.getAddress().equals(value))
.collect(Collectors.toList());
}
// System.out.println(equalList);
// objList.add(equalList);
break;
case "NOT EQUAL":
if (column.equals("name")) {
notEqualList = equalList.stream().filter(li -> !li.getName().equals(value))
.collect(Collectors.toList());
} else {
notEqualList = equalList.stream().filter(li -> !li.getAddress().equals(value))
.collect(Collectors.toList());
}
//System.out.println("AND Group Result --> " + notEqualList);
// objList.add(notEqualList);
break;
default:
System.out.println("No Operator matches");
}
return notEqualList;
}
If you put this code into a static method or in a constructor so it is only executed once, it will build a map where the key is the "column" name and the value is the associated get method from your CustomerPojo class.
final Map<String, Method> getters = Arrays.asList(CustomerPojo.class.getMethods()).stream()
.filter(mth -> mth.getName().startsWith("get")).filter(mth -> !mth.getName().equals("getClass"))
.collect(Collectors.toMap(
mth -> mth.getName().substring(3, 4).toLowerCase() + mth.getName().substring(4), mth -> mth));
The code streams the array of methods from your CustomerPojo class and filters out all but the "getter" methods.
It then collects the remaining methods into a map where the key is the name of the getter after stripping off "get" and lowercasing the first character.
With this you should be able to:
Method mth = getters.get(columnName);

How to sort data in repository into alphabetical order using .stream()

I am trying to create a method that sorts and outputs data I have stored in a repository into alphabetical order using .stream(). Currently I have a method that sorts the data by CityID in numeric order which I will add below. Is there a way to adapt it to sort the same data but by CityName in Alphabetical order?
CityID Method -
private void listCityDataInCityIdOrder() {
System.out.format("\033[31m%s\033[0m%n", "City Id Order");
System.out.format("\033[31m%s\033[0m%n", "=============");
repository.getItems()
.stream()
.sorted()
.map(c -> c.toString())
.forEach(str -> System.out.print(str));
}
Data Set -
1,"Cartagena","Spain",3
"2015",0.2,33,26,6,"S"
"2016",0.0,33,24,8,"SSW"
"2017",0.0,32,25,6,"E"
2,"Glasgow","Scotland",3
"2015",0.0,19,8,3,"SE"
"2016",0.1,21,11,6,"SE"
"2017",2.1,19,11,9,"SW"
City Model Class -
package model;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
/**
*
* #author mga
*/
public class City implements Comparable<City>{
private final int id;
private String cityName;
private String country;
private List<YearData> yearDataCollection;
private static int lastIdAllocated = 0;
static final char EOLN='\n';
static final String QUOTE="\"";
public City() {
this.id = ++lastIdAllocated;
this.cityName = "TBC";
this.country = "TBC";
this.yearDataCollection = new ArrayList<>();
}
public City(String cityName, String country) {
this.id = ++lastIdAllocated;
this.cityName = cityName;
this.country = country;
this.yearDataCollection = new ArrayList<>();
}
public City(String cityName, String country, List<YearData> yearDataCollection) {
this.id = ++lastIdAllocated;
this.cityName = cityName;
this.country = country;
this.yearDataCollection = yearDataCollection;
}
public City(int id, String cityName, String country, List<YearData> yearDataCollection) {
this.id = id;
this.cityName = cityName;
this.country = country;
this.yearDataCollection = yearDataCollection;
if (id > City.lastIdAllocated)
City.lastIdAllocated = id;
}
/**
* #return the id
*/
public int getId() {
return id;
}
// Methods required:
public String getCityName() {
return this.cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
public String getCountry() {
return this.country;
}
public void setCountry(String country) {
this.country = country;
}
public List<YearData> getYearDataCollection() {
return this.yearDataCollection;
}
public void setYearDataCollection(List<YearData> yearDataCollection) {
this.yearDataCollection = yearDataCollection;
}
public void addYearData(YearData yearData) {
this.yearDataCollection.add(yearData);
}
#Override
public String toString() {
return "\nCity Id: " + id + " - City Name: " + cityName +
" - Country: " + country + "\nData: " + yearDataCollection + "\n";
}
public String toString(char delimiter) {
final char EOLN='\n';
final String QUOTE="\"";
String str = Integer.toString(this.id) + delimiter +
QUOTE + this.cityName + QUOTE + delimiter +
QUOTE + this.country + QUOTE + delimiter +
Integer.toString(yearDataCollection.size()) + EOLN;
for (YearData yearData : yearDataCollection) {
str += yearData.toString();
}
return str;
}
public boolean equals(Object object) {
if (this == object) return true;
if (!(object instanceof City)) return false;
if (!super.equals(object)) return false;
City city = (City) object;
return getId() == city.getId() &&
java.util.Objects.equals(getCityName(), city.getCityName()) &&
java.util.Objects.equals(getCountry(), city.getCountry()) &&
java.util.Objects.equals(getYearDataCollection(), city.getYearDataCollection());
}
public int hashCode() {
return Objects.hash(super.hashCode(), getId(), getCityName(), getCountry(), getYearDataCollection());
}
#Override
public int compareTo(City compareCity) {
int cityId =
((City) compareCity).getId();
//ascending order
return this.id - cityId;
//descending order
//return cityId - this.id;
}
public static Comparator<City> CityComparator = new Comparator<City>() {
#Override
public int compare(City city1, City city2) {
String cityName1 = city1.getCityName();
String cityName2 = city2.getCityName();
//ascending order
//return cityName1.compareTo(cityName2);
//descending order
return cityName2.compareTo(cityName1);
}
};
}
sure, change your sorted to:
.sorted(Comparator.comparing(City::getCityName))
or using lambda:
.sorted(Comparator.comparing(c -> c.getCityName()))
You can simplify your Comparator
public static Comparator<City> CityComparator = new Comparator<City>() {
#Override
public int compare(City city1, City city2) {
String cityName1 = city1.getCityName();
String cityName2 = city2.getCityName();
//ascending order
//return cityName1.compareTo(cityName2);
//descending order
return cityName2.compareTo(cityName1);
}
};
to just this :
Comparator<City> cityComparatorSimplified = Comparator
.comparing(City::getCityName).reversed(); // reverse for descending order
and then use it further while sorting as
repository.getItems().stream()
.sorted(cityComparatorSimplified)
.map(Object::toString)
.forEach(System.out::print);

Filter an ArrayList via Optional in Java 8

I am getting an exception while trying to filter and iterate over a Optional using Java 8. I have an object Subject which is being added in an array list and a value of null also.
Problem Statement: I have an ArrayList, I want to iterate it, filter it and then based on that, only print that record which fulfills the condition.
package com.example.app;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class OptionalTest {
public static void main(String[] args) {
System.out.println("getSubject: " + getSubjects());
// print the Subject with the name "Math"
System.out.println("getSubject " + getSubjects().filter((Subject s) -> s.getName().equalsIgnoreCase("Math")));
}
private static Optional getSubjects() {
Subject subject1 = new Subject(1, "Math", (short)2, "");
Subject subject2 = new Subject(2, "Social Science", (short)4, "Social Science");
Subject subject3 = new Subject(3, "English", (short)6, "Literature");
List<Subject> subjects = new ArrayList<>();
Optional<List<Subject>> optional = Optional.of(subjects);
subjects.add(subject1);
subjects.add(subject2);
subjects.add(null);
subjects.add(subject3);
return optional;
}
}
class Subject {
int id;
String name;
short type;
String description;
public Subject(int id, String name, short type, String description) {
this.id = id;
this.name = name;
this.type = type;
this.description = description;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public short getType() {
return type;
}
public void setType(short type) {
this.type = type;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
#Override
public String toString() {
return "\nSubject{" + "id=" + id + ", name=" + name + ", type=" + type + ", description=" + description + '}'+"\n";
}
}
Using Optional.filter would be used to filter List instances as per your code snippet. This is not what you want:
Optional.of(getSubjects()).filter(predicate) //filters lists, not subjects in lists
Your intention is probably to use the a list of Subject objects, then filter. It's the filter method of the Stream interface that returns an Optional instance:
I'd change this method:
private static List<Subject> getSubjects(){
Subject subject1 = new Subject(1, "Math", (short)2, "");
Subject subject2 = new Subject(2, "Social Science", (short)4, "Social Science");
Subject subject3 = new Subject(3, "English", (short)6, "Literature");
List<Subject> subjects = new ArrayList<>();
subjects.add(subject1);
subjects.add(subject2);
subjects.add(null);
subjects.add(subject3);
return subjects;
}
And then use it as follows:
Optional<Subject> filtered = getSubjects()
.stream().filter(s -> s.getName().equalsIgnoreCase("Math"))
//Find first is just one of the many Stream methods
//returning an optional
//It's correct to use it in this case because you know
//only one value is expected to match the filter predicate.
.findFirst();
In fact, if you expect more than one subject to match your filter, you should collect, instead, instead of picking one. In this case, you don't need an optional:
List<Subject> mathSubjects = getSubjects()
.stream().filter((s -> s.getName().equalsIgnoreCase("Math")))
.collect(Collectors.toList());
You can do it very simply using lambda expression, I am providing you a sample so that you can modify according to your need.
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class Java8Optional {
public static void main(String[] args) {
List<Employee> employeeList = new ArrayList<>();
employeeList.add(new Employee(1, "syed"));
employeeList.add(new Employee(2, "az"));
employeeList.add(null);
employeeList.add(new Employee(4, "Rof"));
employeeList.forEach(n -> Optional.ofNullable(n).ifPresent(e -> System.out.println("Employee ID="+e.employeeId+"\tEmployee Name="+e.employeeName)));
}
static class Employee {
Integer employeeId;
String employeeName;
public Integer getEmployeeId() {
return employeeId;
}
public void setEmployeeId(Integer employeeId) {
this.employeeId = employeeId;
}
public String getEmployeeName() {
return employeeName;
}
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
public Employee(Integer employeeId, String employeeName) {
super();
this.employeeId = employeeId;
this.employeeName = employeeName;
}
}
}

Group by n fields in Java using stream API

Need to group List<Object> based upon N property fields, This fields are decided at run time. How can I achieve this ?
Group by multiple field names in java 8
Referred this question, it is using fixed number of fields.
For Example:
Person{ int age, String city, Date doj, double salary}
record1: 25, NYC, 02/25/2018, 50000
record2: 25, MEX, 02/25/2017, 70000
record3: 26, MEX, 02/25/2017, 80000
GroupBy(city, doj)
Record1: = MEX, 02/25/2017, 150000
Record2: = NYC, 02/25/2018, 50000
Salary will added.
I am storing result in Map<Object, List<Object>>
I have achieved most of it. Only problem I am facing is how to alter key in groupingBy.
Collectors.groupingBy( date ) : second iteration will mess data for all city which is to be grouped by city+date.This will be solved if I can alter the key to be City+Date
How can I alter my key in second iteration Collectors.groupingBy( date )
Here is a complete example:
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
public class Grouping {
static final class Person {
private final int age;
private final String city;
private final String doj;
private final double salary;
public Person(int age, String city, String doj, double salary) {
this.age = age;
this.city = city;
this.doj = doj;
this.salary = salary;
}
public int getAge() {
return age;
}
public String getCity() {
return city;
}
public String getDoj() {
return doj;
}
public double getSalary() {
return salary;
}
#Override
public String toString() {
return "Person{" +
"age=" + age +
", city='" + city + '\'' +
", doj='" + doj + '\'' +
", salary=" + salary +
'}';
}
}
enum Property {
AGE {
#Override
protected Object extractValue(Person person) {
return person.getAge();
}
},
CITY {
#Override
protected Object extractValue(Person person) {
return person.getCity();
}
},
DOJ {
#Override
protected Object extractValue(Person person) {
return person.getDoj();
}
};
protected abstract Object extractValue(Person person);
public PropertyValue toValue(Person person) {
return new PropertyValue(this, extractValue(person));
}
}
static final class PropertyValue {
private final Property property;
private final Object value;
public PropertyValue(Property property, Object value) {
this.property = property;
this.value = value;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
PropertyValue that = (PropertyValue) o;
return property == that.property &&
Objects.equals(value, that.value);
}
#Override
public int hashCode() {
return Objects.hash(property, value);
}
#Override
public String toString() {
return "PropertyValue{" +
"property=" + property +
", value=" + value +
'}';
}
}
private static List<PropertyValue> createGroupingKey(List<Property> properties, Person person) {
return properties.stream().map(property -> property.toValue(person)).collect(Collectors.toList());
}
public static void main(String[] args) {
List<Person> persons = Arrays.asList(
new Person(25, "NYC", "02/25/2018", 50000),
new Person(25, "MEX", "02/25/2017", 70000),
new Person(26, "MEX", "02/25/2017", 80000)
);
// TODO ask the user, rather than hardcoding
List<Property> groupingProperties = Arrays.asList(Property.CITY, Property.DOJ);
Map<List<PropertyValue>, Double> salaryAggregatedByChosenProperties =
persons.stream()
.collect(Collectors.groupingBy(p -> createGroupingKey(groupingProperties, p),
Collectors.summingDouble(Person::getSalary)));
System.out.println("salaryAggregatedByChosenProperties = " + salaryAggregatedByChosenProperties);
}
}
What it does:
ask the user which properties should be used for grouping (this is actually not done, but simulated, since that's not the core of your question). You get back a List<Property>, containing (for example) the properties CITY and DOJ
You transform each person into a grouping key, of type List<PropertyValue>, so, the first person will be transformed into [NYC, 02/25/2018], whereas the second and third ones will both be be transformed into [MEX, 02/25/2017] (and thus have the same key).
You group the persons by their key
You sum the salaries of the persons of the same group
Using JB Nizet suggested solution, I have put together an entire working solution in which you can group by n number of fields.
Grouping on any number of fields are possible
Result is independent of grouping field order
User can define aggregation stragery
This nested property will help us store the key for our grouping.
public class NestedProperty {
private final Field property;
private final Object value;
}
Field here is a simple object which will be feed at runtime. We can have better alternative to decide its type.
public class Field{
String name;
Class type;
}
This interface should be implementation by POGO to define what is the aggregation strategy.
public interface Aggregatable<T> {
public void add(T o);
}
Then using NestedProperty object we group the records till n-1 fields using streams.groupby function.
Map<List<NestedProperty>, List<T>> aggregatedRecords = objects.stream()
.collect(Collectors.groupingBy(r -> createGroupingKey(Nminus1fields, r), Collectors.toList()));
private static List<NestedProperty> createGroupingKey(java.util.List<Field> fields, Object r) {
return fields.stream().map(p -> p.toValue(r, p)).collect(Collectors.toList());
}
Then we can run the main aggregation method
List<?> result = objects.stream().filter( r -> r!=null )
.collect(Collectors.groupingBy(
record -> {
try {
return cast.cast(PropertyUtils.getNestedProperty(record, field.getName()));
}catch(Exception e) {
System.out.println("Property not found.");
}
return null;
}))
.entrySet().stream()
.map( e -> e.getValue().stream()
.reduce((f1, f2) -> {
try {
return (T) add(classDefination, f1, f2);
} catch (Exception e1) {
System.out.println("Error is method add()");
}
return null;
})
).map(f -> f.get())
.collect(Collectors.toList());
Please refer to answer in below link:
http://www.unbounded.in/group-by-n-fields-in-java-like-sql-using-streams-api/

Grouping by fields of inner object

I have following two classes:
class Man {
private int id;
private String firstName;
private String lastName;
private int age;
private int countOfChildren;
private Address address;
}
class Address {
private Country country;
private City city;
private String street;
private long quantityOfPeople;
}
I have no idea how to group List<Man> by street and city fields of Address class. How can I do it?
Collectors class provides Collectors.groupingBy(keyProvider, downstream) you can use to group by a pair of values. To pair two values you can either use AbstractMap.SimpleEntry or you can implement your own Pair<T,K> class that represents a pair of two values (it's worth mentioning that you will have to implement hashCode() and equals(object) methods in your Pair class if you want to use it as a key in a hash map). Also two values you want to pair in a key have to implement hashCode() and equals(object) methods - it's worth using immutable classes in this case.
The whole grouping part can be done by:
final Map<Map.Entry<City, String>, List<Man>> groupedByCityAndStreet = people.stream()
.collect(Collectors.groupingBy(
man -> new AbstractMap.SimpleEntry<>(man.getAddress().getCity(), man.getAddress().getStreet()),
Collectors.toList()
));
In this example I have used AbstractMap.SimpleEntry to represent a pair of Country and street. It creates a map where for each key it groups a list of Man object based of country and street. Below you can find a full example:
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
public class GroupByTest {
public static void main(String[] args) {
final List<Man> people = Arrays.asList(
new Man(1, "John", "Doe", 20, 0, new Address(new Country("England"), new City("London"), "Test Street 2", 10000)),
new Man(2, "Mary", "Smith", 54, 4, new Address(new Country("Germany"), new City("Berlin"), "Maine Strasse 32", 10000)),
new Man(3, "James", "Rose", 13, 0, new Address(new Country("England"), new City("London"), "Test Street 2", 10000)),
new Man(4, "Vincent", "Dog", 43, 2, new Address(new Country("Germany"), new City("Berlin"), "Volkswagen Platz 31", 10000)),
new Man(5, "Arnold", "Smoke", 72, 3, new Address(new Country("Italy"), new City("Rome"), "Pepperoni 31", 10000)),
new Man(6, "Katy", "Puppet", 33, 3, new Address(new Country("England"), new City("London"), "Test Street 3", 10000))
);
final Map<Map.Entry<City, String>, List<Man>> groupedByCityAndStreet = people.stream()
.collect(Collectors.groupingBy(
man -> new AbstractMap.SimpleEntry<>(man.getAddress().getCity(), man.getAddress().getStreet()),
Collectors.toList()
));
// Print people associated with given city and street to console
groupedByCityAndStreet.forEach((k, v) -> {
System.out.println("People associated with " + k.getKey().name + ", " + k.getValue() + ":");
v.forEach(man -> {
System.out.println(man);
});
});
}
static final class Man {
private final int id;
private final String firstName;
private final String lastName;
private final int age;
private final int countOfChildren;
private final Address address;
public Man(int id, String firstName, String lastName, int age, int countOfChildren, Address address) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.countOfChildren = countOfChildren;
this.address = address;
}
public int getId() {
return id;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public int getAge() {
return age;
}
public int getCountOfChildren() {
return countOfChildren;
}
public Address getAddress() {
return address;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Man man = (Man) o;
return id == man.id &&
age == man.age &&
countOfChildren == man.countOfChildren &&
Objects.equals(firstName, man.firstName) &&
Objects.equals(lastName, man.lastName) &&
Objects.equals(address, man.address);
}
#Override
public int hashCode() {
return Objects.hash(id, firstName, lastName, age, countOfChildren, address);
}
#Override
public String toString() {
return "Man{" +
"id=" + id +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", age=" + age +
", countOfChildren=" + countOfChildren +
", address=" + address +
'}';
}
}
static class Address {
private final Country country;
private final City city;
private final String street;
private final long quantityOfPeople;
public Address(Country country, City city, String street, long quantityOfPeople) {
this.country = country;
this.city = city;
this.street = street;
this.quantityOfPeople = quantityOfPeople;
}
public Country getCountry() {
return country;
}
public City getCity() {
return city;
}
public String getStreet() {
return street;
}
public long getQuantityOfPeople() {
return quantityOfPeople;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Address address = (Address) o;
return quantityOfPeople == address.quantityOfPeople &&
Objects.equals(country, address.country) &&
Objects.equals(city, address.city) &&
Objects.equals(street, address.street);
}
#Override
public int hashCode() {
return Objects.hash(country, city, street, quantityOfPeople);
}
#Override
public String toString() {
return "Address{" +
"country=" + country +
", city=" + city +
", street='" + street + '\'' +
", quantityOfPeople=" + quantityOfPeople +
'}';
}
}
static class City {
private final String name;
public City(String name) {
this.name = name;
}
public String getName() {
return name;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
City city = (City) o;
return Objects.equals(name, city.name);
}
#Override
public int hashCode() {
return Objects.hash(name);
}
#Override
public String toString() {
return "City{" +
"name='" + name + '\'' +
'}';
}
}
static class Country {
private final String name;
public Country(String name) {
this.name = name;
}
public String getName() {
return name;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Country country = (Country) o;
return Objects.equals(name, country.name);
}
#Override
public int hashCode() {
return Objects.hash(name);
}
#Override
public String toString() {
return "Country{" +
"name='" + name + '\'' +
'}';
}
}
}
When you run this example you will something like this in the console:
People associated with Rome, Pepperoni 31:
Man{id=5, firstName='Arnold', lastName='Smoke', age=72, countOfChildren=3, address=Address{country=Country{name='Italy'}, city=City{name='Rome'}, street='Pepperoni 31', quantityOfPeople=10000}}
People associated with London, Test Street 3:
Man{id=6, firstName='Katy', lastName='Puppet', age=33, countOfChildren=3, address=Address{country=Country{name='England'}, city=City{name='London'}, street='Test Street 3', quantityOfPeople=10000}}
People associated with Berlin, Volkswagen Platz 31:
Man{id=4, firstName='Vincent', lastName='Dog', age=43, countOfChildren=2, address=Address{country=Country{name='Germany'}, city=City{name='Berlin'}, street='Volkswagen Platz 31', quantityOfPeople=10000}}
People associated with Berlin, Maine Strasse 32:
Man{id=2, firstName='Mary', lastName='Smith', age=54, countOfChildren=4, address=Address{country=Country{name='Germany'}, city=City{name='Berlin'}, street='Maine Strasse 32', quantityOfPeople=10000}}
People associated with London, Test Street 2:
Man{id=1, firstName='John', lastName='Doe', age=20, countOfChildren=0, address=Address{country=Country{name='England'}, city=City{name='London'}, street='Test Street 2', quantityOfPeople=10000}}
Man{id=3, firstName='James', lastName='Rose', age=13, countOfChildren=0, address=Address{country=Country{name='England'}, city=City{name='London'}, street='Test Street 2', quantityOfPeople=10000}}
Hope it helps.

Categories