I have multiple streams of Student Object. I have to merge them in sorted order.
class Student {
private String branch;
private Integer rollNo;
private Date doj;
// Getters and Setters
}
In another class, I have a method
private Stream<Student> mergeAndSort(Stream<Student> s1, Stream<Student> s2, Stream<Student> s3) {
return Stream.of(s1, s2, s3).sorted(...
// I tried this logic multiple times but is not working. How can I inject Student comparator here.
// I have to sort based on branch and then rollNo.
);
}
Stream.of(s1, s2, s3) gives you a Stream<Stream<Student>>. In order to get a Stream<Student>, use flatMap:
Stream.of(s1, s2, s3).flatMap(Function.identity()).sorted(...)...
To sort according to the required properties:
return Stream.of(s1, s2, s3)
.flatMap(Function.identity())
.sorted(Comparator.comparing(Student::getBranch).thenComparing(Student::getRollNo));
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 12 months ago.
Improve this question
class Person{
private String name;
private String address;
private int id;
private int uniqueIdentificationNumber;
}
class Company{
private String name;
private String address;
private int id;
private int uniqueIdentificationNumber;
private String company;
}
class Test{
public static void main(String[] args)
{
List<Person> persons = new ArrayList<>();
Person p1 = new Person();
p1.setName("ABC");
p1.setAddress("US");
p1.setId(1);
p1.setUniqueIdentificationNumber(11);
Person p2 = new Person();
p2.setName("PQR");
p2.setAddress("US");
p2.setId(2);
p2.setUniqueIdentificationNumber(22);
persons.add(p1);
persons.add(p2);
List<Company> companies = new ArrayList<>();
Company c1 = new Comapny();
c1.setName("ABC");
c1.setAddress("US");
c1.setId(3);
c1.setUniqueIdentificationNumber(44);
c1.setCompany("C1")
Company c2 = new Comapny();
c2.setName("ABC");
c2.setAddress("US");
c2.setId(1);
c2.setUniqueIdentificationNumber(11);
c2.setCompany("C2");
companies.add(c1);
companies.add(c2)
}
}
I want to compare two different object types of lists (companies and persons) with Java8 Stream API and return the customer object which is matching with Id and setUniqueIdentificationNumber. i.e here in this case it should retun c2.
Can anyone help on this
It doesn't clear how uniqueIdentificationNumber of the Person and Company are related. It's worth to refine these classes to represent the relationship between them in a better way (maybe a company can hold a reference to a list of customers). And don't overuse setters, if id is unique there's no need to allow it to be changed.
Although it's not clear how these values are connected because of the drawbacks of your class design technically it's doable.
return the customer object which is matching with Id
For that, you need to create two maps that will associate these identifiers with companies and persons. Then create a stream over the keys of one of these maps and check for every key whether if contained in another map. And then retrieve the Person objects for filtered keys and collect the result into a list.
Map<Integer, Person> personById =
persons.stream()
.collect(Collectors.toMap(Person::getUniqueIdentificationNumber,
Function.identity()));
Map<Integer, Company> companyById =
companies.stream()
.collect(Collectors.toMap(Company::getUniqueIdentificationNumber,
Function.identity()));
List<Person> customers =
personById.keySet().stream()
.filter(companyById::containsKey) // checking whether id is present in the company map
.map(personById::get) // retrieving customers
.collect(Collectors.toList());
Update
Let me rephrase the description of the problem.
There are two unrelated classes A and B. Both classes have two fields of type int, let's say val1 and val2 (and maybe a few more fields but we are not interested in them).
We have a list of objects A and a list of objects B. The goal is to find a single object A for which exists an object B with the same values for both val1 and val2 (in order to keep things simple I propose to stick with this example).
There are two approaches that could be used for that purpose:
create an auxiliary class with two fields val1 and val2, and associate every instance of A and B with instances of this class;
create a nested map Map<Integer, Map<Integer, *targetClass*>>, this solution is more complicated and less flexible, if you'll need to compare objects by three, four, etc. fields the code will quickly become incomprehensible.
So I'll stick with the first approach. We need to declare the ValueHolder class with two fields and implement the equals/hashCode contract based on these fields. For Java 16 and above we can utilize a record for that purpose and make use of equals(), hashCode, getters provided by the compiler. The option with the record will look like this:
public record ValueHolder(int val1, int val2) {} // equals/hashCode, constructor and getters provided by the compiler
Classes A and B
public class A {
private int val1;
private int val2;
// constructor and getters
}
public class B {
private int val1;
private int val2;
// constructor and getters
}
And a method that accepts two lists: List<A> and List<B>, and return a result as Optional<A>. Because the matching element may or may not be present and it's a good practice to return an optional object in such cases instead of returning null in case the result was not found. It provides more flexibility and that's precisely the case for which the optional was designed.
public Optional<A> getMatchingItem(List<A> listA, List<B> listB) {
Map<ValueHolder, A> aByValue = listA.stream()
.collect(Collectors.toMap(a -> new ValueHolder(a.getVal1(), a.getVal2()),
Function.identity()));
Map<ValueHolder, B> bByValue = listB.stream()
.collect(Collectors.toMap(b -> new ValueHolder(b.getVal1(), b.getVal2()),
Function.identity()));
return aByValue.keySet().stream()
.filter(bByValue::containsKey)
.findFirst()
.map(aByValue::get);
}
I have an object with attributes like below:
public class Model1 {
private String aa;
private List<String> bb;
private List<CC> list;
}
public class CC{
private String cc1;
private List<String> cc2;
}
When I use javers, I am able to get changes made to aa and bb. But for List<CC> it just says, list/2 was added or list/0 was removed but it does not give exact data that was added or removed.
How do I get the complete data of CC object which was added, removed or changed from list?
I have the following data structure
public class Zones {
private List<Zone> zones;
}
public class Zone {
private int id;
private String name;
private List<Part> parts;
}
public class Part {
private int id;
private String name;
}
THis is my problem. I have with me an instance of Zones, say z.
I want to stream z and do the following:
construct a map out of z with the following conditions:
if the key (based on the "Id" of the Zone) is new, then create an entry in the map with the key and the Zone.
If the key is a duplicate, then append all "parts" of this duplicate zone into the existing zone's parts list.
In the end I should have a map with "Id" of the zone as the key and the zone as the value.
How can I do this in Java8 using streams?
You can use Collectors.toMap(), to make a new Map with zone id as the key and Zone as the value, if there is a duplicate then take the List<Part> from the second Zone and append it to the first one:
Map<Integer, Zone> map = z.getZones().stream()
.collect(Collectors.toMap(Zone::getId, Function.identity(),
(zone1, zone2) -> {
zone1.getParts().addAll(zone2.getParts());
return zone1;
}));
I have a database table which holds Metadata for documents. My task now is to get a list with documenttypes. The documenttypes are not unique in the database table but of course I want them to be in my list. The sql is very simple:
SELECT DISTINCT groupname, group_displayorder
FROM t_doc_metadata
ORDER BY group_displayorder;
I have learned that I can use projections to get a subset of fields from my entity DocMetadata. I solved this as follows. My Entity:
#Entity
#Table(name="T_DOC_METADATA")
#Data
public class DocMetadata {
..............
#Column(nullable=false)
private String displayname;
#Column(nullable=false)
private Integer displayorder;
#Column(nullable=false)
private String groupname;
#Column(name="GROUP_DISPLAYORDER",
nullable=false)
private Integer groupDisplayorder;
#Column(name="METADATA_CHANGED_TS",
nullable=false,
columnDefinition="char")
private String metadataChangedTimestamp;
..........
}
My inteface for projection:
public interface GroupnameAndOrder {
String getGroupname();
Integer getGroupDisplayorder();
void setGroupname(String name);
void setGroupDisplayorder(int order);
}
Now I thought I'd be extraordinary clever by adding these lines to my repository:
#Query("select distinct d.groupname, d.groupDisplayorder from DocMetadata d order by d.groupDisplayorder")
public List<GroupnameAndOrder> findSortedGroupnames();
Sadly, when iterating over the result list and calling getGroupname() the result is null.
So I changed the lines in my repository according to the documentation:
public List<GroupnameAndOrder> findBy();
Now I get the groupnames but of course they are not unique now. So it doesn't solve my problem.
Is there any way to receive a ordered list with unique groupnames?
You are trying to be too clever. Instead just write the proper find method and return the GroupnameAndOrder. Spring Data JPA will then only retrieve what is needed for the projection.
Something like this should do the trick.
List<GroupnameAndOrder> findDistinctByOrderByGroupDisplayorder();
I have two classes :
public class Invoice {
private int InvoiceId;
private Customer customer;
private Date invoiceDate;
}
And
public class Customer {
private int customerId;
private String customerType;
}
customerType field can have three values : F,H,B
I have an arrayList of Invoice objects, i want to sort it using the customerType property in a specific order :all the the invoice with customerType "F" then those with "H" then "B".
I tried to implement the Comparator interface , but the problem is that his method compare takes two arguments of Customer objects and the sorting is made in ascending or descending way.
This is the code :
Collections sort(invoicesList, new Comparator<Invoice >() {
public int compare(Invoice s1,Invoice s2) {
return s1.getCustomer().getCustomerTpe().equalsIgnoreCase("F").compareTo(s2.getCustomer().getCustomerTpe().equalsIgnoreCase("H"))
}
}
);
Is there a way to sort my collection with a specific order with three fileds value ?
Thnks
One way to customize the sort order is to first map the arguments and then sort on the mapped values. In your case mapping of the customerType can be done by finding its index in the string "FHB". In Java 8, this might be expressed as
Collections.sort(invoicesList, Comparator.comparingInt(
(invoice) -> "FHB".indexOf( invoice.getCustomer().getCustomerType() )));