Sorting collections within objects using Comparator and functions - java

I have the below method, and would like to add another field to the comparison, however I am finding it difficult to include.
private static int daoComparator(EDao eDao1, EDao eDao2) {
return Comparator.comparing((EDao eDao) -> eDao.getObjectA().getStatus())
.thenComparing(EDao::getUpdatedDate)
.thenComparing(EDao::getCreatedDate)
.thenComparing((EDao eDao) -> eDao.getObjectA().getId())
.compare(eDao1, eDao2);
}
Within EDao class, there is ObjectA, and within ObjectA, there is a Collection<ObjectB>. Within ObjectB there is an Enum of type String which I need to retrieve and sort within the above daoComparator. How can I update my method to retrieve and sort this String Enum? It also needs to be the first comparison. Comparator is from java.util package.
public class EDao
{
private ObjectA objectA;
private Date updatedDate;
private Date createdDate;
}
public class ObjectA
{
private String id;
#Enumerated(EnumType.STRING)
private String status;
private Collection<ObjectB> objectB;
}
public class ObjectB
{
// field I want to retrieve for sorting
#Enumerated(EnumType.STRING)
String RegStatus status;
}

I managed to retrieve the status', I just need to handle the scenario where ObjectB could be null.
private static int daoComparator(EDao eDao1, EDao eDao2) {
return Comparator.comparing((EDao eDao) -> eDao.getObjectA().getObjectB().stream().findFirst().get().getStatus())
.thenComparing((eDao eDao) -> eDao.getObjectA().getStatus())
.thenComparing(EDao::getUpdatedDate)
.thenComparing(EDao::getCreatedDate)
.thenComparing((EDao eDao) -> eDao.getObjectA().getId())
.compare(eDao1, eDao2);

Related

Setting null or empty for #singular list in lombok

I have a list as
#Value
#Builder(toBuilder = true)
class Demo {
private BigInteger id;
#Singular
private List<String> name;
}
I have some data added in the name list. Now I want to set it to empty or null. How can I achieve it?
As per my understanding if I m using #Singular annotation it makes the list immutable.
I m using Java with Lombok.
As per my understanding if I m using #Singular annotation it makes the
list immutable.
It makes the list in the Demo instance immutable. It does not make the list immutable in the builder; it simply changes the API of the builder.
From the documentation:
with the #Singular annotation, lombok will treat that builder node
as a collection, and it generates 2 'adder' methods instead of a
'setter' method
As for emptying the list,
A 'clear' method is also generated.
In your case, clearNames (the list field should be called names, not name, otherwise Lombok complains).
If you want a mutable instance, don't use #Singular
As per my understanding if I m using #Singular annotation it makes the
list immutable
No, Lambok just create method for single add element, for you class (I strongly recommend change name to names). You just need to call method "clearNames"
#Value
#Builder(toBuilder = true)
class Demo {
private BigInteger id;
#Singular
private List<String> names;
}
Lambok generate following builder
public static class UserBuilder {
private BigInteger id;
private ArrayList<String> names;
UserBuilder() {
}
public User.UserBuilder name(String name) {
if (this.names == null) this.names = new ArrayList<String>();
this.names.add(name);
return this;
}
public User.UserBuilder names(Collection<? extends String> names) {
if (this.names == null) this.names = new ArrayList<String>();
this.names.addAll(names);
return this;
}
public User.UserBuilder clearNames() {
if (this.names != null)
this.names.clear();
return this;
}
...
public User build() {
...
}
public String toString() {
...
}
}

Java 8 sort multiple objects

public class B {
private String name;
private String value;
//Setters and Get
}
public class C {
private String name;
private String value;
//Setters and Get Methods
}
public class D {
private String name;
private String value;
//Setters and Get
}
public class A {
private B b;
private C c;
private D d;
// Setters and Get
}
public class Example{
List<A> a = new Array List<A>();
//Lets assume a will contain objects of class B, C and D
a .sort( Comparator.comparing(A::getB().getName).thenComparing(A::getC().getName));
}
Sort field from one pojo , then sort field by next pojo.
Need to understand how to sort in this situation. Can we use
Comparator.comparing ()in this case?
You can't use method refences like that, but you could just use lambda expressions:
a.sort(Comparator.comparing((A x) -> x.getB().getName())
.thenComparing(x -> x.getC().getName()));

How to Map an object contains a List with Mapstruct

Given the Source class as defined below:
class Source{
private String name;
private int age;
private List<Phone> phones;
// getters and setters
}
and the Phone class as defined below:
class Phone{
private Long id;
private String phoneNumber;
// getters and setters
}
and the Target class as defined below:
class Target{
private String name;
private int age;
private List<Long> idsPhones;
// getters and setters
}
I have an interface is:
#Mapper
interface MyMapper{
Target toTarget(Source source);
Source toSource(Target target);
}
How can I map the List of Phones from the Source class to a List of idsPhones in the Target Class and vice versa?
In order to achieve this you need to help MapStruct a bit by telling how to map from Phone into Long. And the reverse as well.
Your mapper needs to look something like:
#Mapper(uses = PhoneRepository.class)
interface MyMapper {
#Mapping(target = "idsPhones", source = "phones")
Target toTarget(Source source);
#InheritInverseMapping
Source toSource(Target target);
default Long fromPhone(Phone phone) {
return phone.getId();
}
}
If your PhoneRepository contains a method that accepts a Long and returns Phone then MapStruct will automatically know what to do and invoke that method.

How to pass different objects (having same parent) to a constructor and how to identify type of that object in constructor

I have a scenario where I'm sending different objects to a constructor and I have to identify the class of that object and set values accordingly.
Class ABC {
private long id;
private SomeClass obj;
private String xyzName;
private Date date;
private EnumType status;
// And Getters and Setters
}
Class A extends ABC {
private String someOtherId;
private String type;
private String model;
private String manufacturer;
//and some props and Getters and Setters
}
Class B extends ABC {
private String someOtherId;
private String equipName;
private String model;
private String serialNo;
//and some props and Getters and Setters
}
Class C extends ABC {
private String someOtherId;
private String materialName;
private String desc;
private String serialNo;
//and some props and Getters and Setters
}
Note: These are the entity classes
And In Controller, I'm doing ops like adding, editing, updating(mostly changing statuses) and etc.And every time I do I have to enter or log kind of msg into
History Class. something like
From AController
historyService.enterLogToHistory(new
History(Aobject, EnumType.somestatus));
From BController,
historyService.enterLogToHistory(new History(Bobject,
EnumType.somestatus));
From CController,
historyService.enterLogToHistory(new
History(Cobject,EnumType.somestatus));
Class History() {
private long id;
private Date date;
private String Status;
private String Activity; // or msg
// some other #Transient properties
History(Object obj) {
//set above values like by getting values form this obj(using getters)
}
History(Object obj, EnumType status) {
this(obj);
// set some other #Transient properties by getting values form this obj
// and set value for msg;
}
}
So, Now my problem is how to identify that object whether it is A obj,
B, obj or C obj because if know the type of object only I can the getters of that obj and I can set values in History constructor.
So, please anyone help me out in this
To get rid of instanceof mess, consider using oveloaded constructors:
class History {
History(A a) {
// initialize by `A` instance
}
History(B b) {
// initialize by `B` instance
}
//...
}
This will work if you know classes of all instances on compile time.
Another option is to switch to static factory methods:
public class History {
// private constructor to hide instance creation
private History(ABC abc) {
this.id = abc.getId();
this.date = new Date(abc.getDate().getTime());
// ... another common properties
}
public History setStatus(Status s) {
this.status = s;
return this;
}
// public static factory methods to create instances specified by input
public static History of(A a) {
History h = new History(a);
h.type = a.getType();
// ... properties specific for `A`
return h;
}
public static History of(A a, Status status) {
return of(a).setStatus(status);
}
public static History of(B b) {
History h = new History(b);
h.model = b.getModel();
// ... properties specific for `B`
return h;
}
// ...
}
Then, to create History instances, caller invokes:
History aHistory = History.of(a);
History bHistory = History.of(b, Status.ACTIVE);
The main advantage of such approach is that more stable API is introduced, while it remains flexible for internal refactoring. Imagine, you decide to implement various behavior for History of different types, e.g. VehicleHistory and VesselHistory. So you create those classes extending History, override some methods in them and refactor a few of() methods in History class (so that not new History(), but new VehicleHistory() is called). As far as you never call constructor externally, for outer code things remain unchanged -- it receives History object as before from the same History.of() method as before.

How to return an EnumMap if the keys are private

Here's a snippet of code I'm working on:
package testPack.model;
public class Person {
private static enum Field {
NAME, ALIASES, DATE_OF_BIRTH, DATE_OF_DEATH;
}
private EnumMap<Field, Optional<Instant>> lastUpdateTime;
private Name name;
private ArrayList<String> aliases;
private Optional<LocalDate> dateOfBirth;
private Optional<LocalDate> dateOfDeath;
public final EnumMap<Field, Optional<Instant>> getUpdateTimes() {
// Return a copy to avoid external changes to person
return new EnumMap<Field, Optional<Instant>>(lastUpdateTime);
}
}
The purpose of the Field enum is to keep track of the last time the fields of Person were updated. How can I make it so that another class can call getUpdateTimes and operate on the return without having access to Fields. To be specific, I want to be able to iterate over the values as well as get a specific value. Do I need to make Field public, and if so how can I use field without needing to import it (i.e. how do I avoid having to do import testpack.model.Person.Field)?
You are at least going to have to import the Person class. If you make the enum public, you should be able to access it from a class in a different package (and iterate through the keys as you say) as follows:
import testPack.model.Person;
...
Person person = new Person();
...
Map<Person.Field, Optional<Instant>> updateTimes = person.getUpdateTimes();
for (Person.Field field : updateTimes.keySet())
{
... // do something with the key
}
You can avoid the import, if that's really a requirement, by putting the two classes in the same package.
How about doing this?
Have the enum in Field.java
package testPack.model;
public enum Field {
NAME, ALIASES, DATE_OF_BIRTH, DATE_OF_DEATH;
}
Have a separate Person.java file
package testPack.model;
public class Person {
private static Field field;
private EnumMap<Field, Optional<Instant>> lastUpdateTime;
private Name name;
private ArrayList<String> aliases;
private Optional<LocalDate> dateOfBirth;
private Optional<LocalDate> dateOfDeath;
public final EnumMap<Field, Optional<Instant>> getUpdateTimes() {
// Return a copy to avoid external changes to person
return new EnumMap<Field, Optional<Instant>>(lastUpdateTime);
}
}
And, import the packages.class accordingly.
Also, it the makes the code loosely coupled

Categories