I would like to merge two lists of objects in Java. One is a list of objects from a database and the second list is from Excel.
The first list of objects contains data like:
[name(str),valid_from(date),valid_to(date),active(flag)]
Objects are equal if names, active flags are equal and dates of obj1,obj2 - valid_from,valid_to are disjoint intervals.
There can be two elements on the list with overlapping date intervals and the same name only if active flag is not equal.
The second list of objects contains the same data and also information about whether to update, remove or create new record on the first list:
[name(str),valid_from(date),valid_to(date),active(flag), action_type(c/u/d)]
//edit: my code is:
public class ClassA {
private String name;
private Date validFrom;
private Date validTo;
private boolean active;
public ClassA(String name, Date validFrom, Date validTo, boolean active) {
this.name = name;
this.validFrom = validFrom;
this.validTo = validTo;
this.active = active;
}
public String getName() {
return name;
}
public Date getValidFrom() {
return validFrom;
}
public Date getValidTo() {
return validTo;
}
public boolean isActive() {
return active;
}
public static void main(String[] args) {
List<ClassA> objectsFromDB = getObjectsFromDB();
List<ClassB> objectsFromFile = getObjectsFromFile();
Map<String, ClassA> objectNameToClassA = buildObjectNameToClassAMap(objectsFromDB);
List<ClassA> objectsToCreate = new ArrayList<ClassA>();
List<ClassA> objectsToUpdate = new ArrayList<ClassA>();
List<ClassA> objectsToDelete = new ArrayList<ClassA>();
for(ClassB object: objects) {
ActionType actionType = object.getActionType();
if(ActionType.CREATE.equals(actionType) {
objectsToCreate.add(object.getObjectA());
}
if(ActionType.UPDATE.equals(actionType) {
objectsToUpdate.add(object.getObjectA());
}
if(ActionType.DELETE.equals(actionType) {
objectsToDelete.add(object.getObjectA());
}
}
}
private static Map<String, ClassA> buildObjectNameToClassAMap(List<ClassA> objects) {
Map<String, ClassA> result = new LinkedHashMap<String, ClassA>();
for(ClassA object: objects) {
result.put(object.getName(), object);
}
return result;
}
}
enum ActionType {
CREATE, UPDATE, DELETE;
}
class ClassB {
private ClassA classA;
private ActionType actionType;
public ClassB(ClassA classA, ActionType actionType) {
this.classA = classA;
this.actionType = actionType;
}
public ClassA getClassA() {
return classA;
}
public ActionType getActionType() {
return actionType;
}
}
I would first define a class for your objects where you override the equals method in order to respect your conditions of equality. Then I would use one of the classes of java.util that implement the Set interface, HashSet for example. Then use the add method on this set with your objects. In set arithmetics, adding is a set union and so it performs a merge.
Related
I have this condition (property rent system, rent is counted per night)
Owner has one or more property. Property has description, price, and isOccupied attribute.
The property can be: hotel (with 3 room types), flat/apartment, and house for homestay.
Through a registry function, a customer can order one or more property available at certain date.
Here are the pre-defined conditions for registry function:
There are 2 registered owners and customers in the system.
Owner 1 has 10 hotel rooms (standard type) for US$30 per night and 3 hotel rooms (suite type) for US$60 per night.
Owner 2 has 3 apartments for US$70 per night and 5 homestay house for US$20 per night.
Customers can rent one or more owner's property for a certain date.
To model the property, I use inheritance concept. For now, it looks something like this.
Property.java
public class Property {
private String description;
private int propertyPrice;
private String ownerName; // should it be here? or should it be made in another class?
private boolean isOccupied;
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public int getPropertyPrice() {
return propertyPrice;
}
public void setPropertyPrice(int propertyPrice) {
this.propertyPrice = propertyPrice;
}
}
Hotel.java
public class Hotel extends Property {
private String[] roomType;
private int[] roomCount;
public Hotel(){
this.roomType = new String[]{"Standard", "Deluxe", "Suite"};
this.roomCount = new int[]{0, 0, 0};
}
public String[] getRoomType() {
return roomType;
}
public void setRoomType(String[] roomType) {
this.roomType = roomType;
}
public int[] getRoomCount() {
return roomCount;
}
public void setRoomCount(int[] roomCount) {
this.roomCount = roomCount;
}
}
Apartment.java
public class Apartment extends Property {
private int roomCount;
public int getRoomCount() {
return roomCount;
}
public void setRoomCount(int roomCount) {
this.roomCount = roomCount;
}
}
Homestay.java
public class HomestayRoom extends Property {
private String parentName;
public String getParentName() {
return parentName;
}
public void setParentName(String parentName) {
this.parentName = parentName;
}
}
What makes me confused is, how can I define the pre-defined conditions for registry to model owner-property relation? Should I make the owner at another class? If so, how can I relate the properties and its owner?
Most of what you have done is correct, but you could also have a property type enum
public enum PropertyType{
HOTEL,APARTMENT,HOMESTAY
}
Now you're super class would be
public class Property {
private String description;
private int propertyPrice;
private String ownerName;
private boolean isOccupied;
private PropertyType pt;
....
}
A constructor for hotel would be
public Hotel(){
this.roomType = new String[]{"Standard", "Deluxe", "Suite"};
this.roomCount = new int[]{0, 0, 0};
super(PropertyType.HOTEL);
}
Similarly you could have constructors for Homestay and Apartment, with the extra line of super(PropertyType.HOMESTAY) and super(PropertyType.APARTMENT) respectively.
I'm using ModelMapper to map a JPA entities to DTO. I have Collections on entities
The dto is generated by wsimport from a wsdl file, but the collection's setters aren't generate
public class sampleEntity{
private String name;
private Collection<String> list;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Collection<String> getList() {
return list;
}
public void setList(Collection<String> list) {
this.list = list;
}
}
public class sampleDTO{
private String name;
private Collection<String> list;
//getters & setters
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Collection<String> getList() {
return list;
}
//no collection setters with jaxb!!! Use getList().add()
}
I use a simple MapperUtils to map entities and dto
public class MapperUtils {
private static ModelMapper modelMapper = new ModelMapper();
static {
modelMapper = new ModelMapper();
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.LOOSE);
}
private MapperUtils() {
}
public static <D, T> D map(final T entity, Class<D> outClass) {
return modelMapper.map(entity, outClass);
}
public static <D, T> List<D> mapAll(final Collection<T> entityList, Class<D> outCLass) {
return entityList.stream().map(entity -> map(entity, outCLass)).collect(Collectors.toList());
}
public static <S, D> D map(final S source, D destination) {
modelMapper.map(source, destination);
return destination;
}
}
So how to use ModelMapper to use DTO.getXXXX.add () if Entity.XXXX is a Collection?
I don't have any idea if ModelMapper is able to support getList().add() call over a destination during the mapping.
Here are 2 approached that might solve your problem.
Approach 1: enable the field matching
modelMapper.getConfiguration()
.setFieldAccessLevel(AccessLevel.PRIVATE)
.setFieldMatchingEnabled(true);
Approach 2:
Try to generate the setter code with wsimport.
(I'm a beginner so this may sound obvious/lack information.) I have an ArrayList of attributes for different pets including attributes such as their given-name, common-name, the price of the animal, sex, date bought and date sold. this information is generated from a separate class that adds an array of information to an array of arrays of the already existing list of animals. Essentially, I want to send the array to another class (called Pets) so it can then be added to the array of arrays. I understand this may sound confusing but this is the only way I can word it, I can clarify anything if needed. Any help would be great as I'm really stuck and can't work out how to send it. This is the code that generates my values in the array (using text-boxes to input the information).
public void actionPerformed(ActionEvent e) {
ArrayList<String> NewanimalArr = new ArrayList<>();
String givenName = txtGivenname.getText();
String commonName = txtCommonName.getText();
String priceOf = txtPrice_1.getText();
String sexOf = txtSex.getText();
String colourOf = txtMaincolour.getText();
String dateOfA = txtArrivaldate.getText();
String dateSold = txtSellingdate.getText();
NewanimalArr.add(givenName);
NewanimalArr.add(commonName);
NewanimalArr.add(priceOf);
NewanimalArr.add(sexOf);
NewanimalArr.add(colourOf);
NewanimalArr.add(dateOfA);
NewanimalArr.add(dateSold);
System.out.println(NewanimalArr);
}
});
this will then print information generated that is entered for example:
[alex, Dog, 40.50, Male, Brown, 14/04/2015, 14/12/2016]
how do I then send this data to another class
Option one Constructor Injection:
public class Foo {
List<String> actionPerformed(ActionEvent e) {
List<String> newanimalArr = new ArrayList<>();
.....
return newanimalArr
}
...
public class Pets {
private final List<String> array;
public Pets(final List<String> array) {
this.array = array;
}
void bar() {
System.out.println(this.array);
}
}
....
public static void main(String[] args) {
Foo foo = new Foo();
Pets pets = new Pets(foo.actionPerformed( new ActionEvent() ) );
pets.bar();
}
Option two Getter-Setter Injection:
public class Foo {
private final List<String> newanimalArr;
public Foo() {
this.newanimalArr = new ArrayList<>();
}
public void actionPerformed(ActionEvent e) {
.....
}
public List<String> getNewanimalArr() {
return new ArrayList<String>(newanimalArr);
}
}
...
public class Pets {
private List<String> array;
public Pets() {
this.array = Collections.<String>emptyList();
}
public void setArray(final List<String> array) {
this.array = array;
}
public void bar() {
System.out.println(this.array);
}
}
....
public static void main(String[] args) {
Foo foo = new Foo();
foo.actionPerformed( new ActionEvent() );
Pets pets = new Pets();
bar.setArray( foo.getNewanimalArr() );
pets.bar();
}
See also Dependency Injection Patterns
Create a class definition of Pet, using instance variables for the fields. In Java it is custom to create a setXyz and a getXyz for each xyz field. You can also create a constructor in which you pass all the values and assign them to the fields, this minimizes the risk of fields not being filled in.
The initial ArrayList you are creating doesn't add that much use, it is easier to create the Pet instances directly:
List<Pet> newArrivals = new ArrayList<>();
// get data from view fields and if necessary transform them to other objects such as:
LocalDate arrivedOn = LocalDate.parse(txtArrivaldate.getText(), DateTimeFormatter.ofLocalizedDate(FormatStyle.FormatStyle);
// create and add a new Pet object to the list
newArrivals.add(new Pet(.....));
public class Pet {
public enum Gender {
FEMALE, MALE
}
private String givenName;
private String commonName;
private double price;
private Gender gender;
private String color;
private LocalDate arrivedOn;
private LocalDate soldOn;
public Pet() {
}
public Pet(String givenName, String commonName, double price, Gender gender, String color, LocalDate arrivedOn,
LocalDate soldOn) {
super();
this.givenName = givenName;
this.commonName = commonName;
this.price = price;
this.gender = gender;
this.color = color;
this.arrivedOn = arrivedOn;
this.soldOn = soldOn;
}
public String getGivenName() {
return givenName;
}
public void setGivenName(String givenName) {
this.givenName = givenName;
}
public String getCommonName() {
return commonName;
}
public void setCommonName(String commonName) {
this.commonName = commonName;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public Gender getGender() {
return gender;
}
public void setGender(Gender gender) {
this.gender = gender;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public LocalDate getArrivedOn() {
return arrivedOn;
}
public void setArrivedOn(LocalDate arrivedOn) {
this.arrivedOn = arrivedOn;
}
public LocalDate getSoldOn() {
return soldOn;
}
public void setSoldOn(LocalDate soldOn) {
this.soldOn = soldOn;
}
}
In my project, I'm facing to a problem of JSON serialization. I have made a quick simple example to illustrate it:
I have created an object Session which contains a hashMap.
public class session {
private Map<String, Object> mapKeyValue = new HashMap<>();
public Map<String, Object> getMapKeyValue() {
return mapKeyValue;
}
public void setMapKeyValue(Map<String, Object> mapKeyValue) {
this.mapKeyValue = mapKeyValue;
}
}
This map can contains various object. I only know the type of these objects at runtime. Example :
public class Dog {
private final String race;
private final String name;
public Dog(String race, String name) {
this.name = name;
this.race = race;
}
public String getName() {
return name;
}
public String getRace() {
return race;
}
}
or
public class Insect {
private final int nbPates;
private final String name;
public Insect(int nbPates, String name) {
this.nbPates = nbPates;
this.name = name;
}
public int getNbPates() {
return nbPates;
}
public String getName() {
return name;
}
}
I want to save my object session to a database to be able to restore it later.
My first idea was to serialize my session object in json and then to save to to database.
public static void main(String[] args) {
final session session = new session();
session.add("Dog", new Dog("labrador", "milou"));
session.add("Insect", new Insect(4, "cafar"));
String output = serializeJSON(session);
final session session2 = (session) deserializeJSON(output, session.class);
Dog dog = (Dog)session2.get("Dog");
}
It works well. When i deserialize it, there is no error, but as a result, the map contained in the deserialized Session doesn't contain the original type of object but contains hashMap$Node objects.
I guess the deserialization can not guess the types of objects from the string.
Is there a workAround or any other strategy to save my session to database and to be able to restore it as it was previously ?
I am using a custom class object as the key for a HashMap. In this class definition, I have overridden the equals() and hashCode() methods.
public class TimeTableDataModel {
Map <Course, List <Timings>> tm;
TimeTableDataModel() {
tm = new HashMap<>();
}
void addCourseItem(Course course) {
tm.put(course, new ArrayList<Timings>());
}
void addNewTimeTableItem(Course course, Timings newTiming) {
List <Timings> t;
if(!tm.containsKey(course)) {
addCourseItem(course);
}
t = tm.get(course);
t.add(newTiming);
tm.put(course, t);
}
public static final class Course {
private final String courseCode;
private final String courseName;
private final String section;
private final String group;
Course(String code, String courseName, String section, String group) {
this.courseCode = code;
this.courseName = courseName;
this.section = section;
this.group = group;
}
public String getCourseCode() { return courseCode; }
public String getCourseName() { return courseName; }
public String getSection() { return section; }
public String getGroup() { return group; }
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Course)) {
return false;
}
Course otherObj = (Course) obj;
return Objects.equals(courseCode,otherObj.courseCode)
&& Objects.equals(courseName, otherObj.courseName)
&& Objects.equals(section, otherObj.section)
&& Objects.equals(group, otherObj.group);
}
#Override
public int hashCode() {
return Objects.hash(courseCode, courseName, section, group);
}
}
public static class Timings {
String time;
String day;
String room;
Timings(String time, String day) {
setTime(time);
setDay(day);
}
public String getTime() { return time; }
public String getday() { return day; }
public void setTime(String time) { this.time = time; }
public void setDay(String day){this.day = day;}
}
}
In above code I have created Course class to be used as the key for the HashMap and using a List<Timings> for values. What I intend is to get a List of timings when a Course is passed to hm.get(course). So far I can get a keyset then sequentially get values for each course.
for(Course c : timetable.tm.keySet()) {
System.out.println(c.getCourseCode() + " " + c.getCourseName());
for(Timings t : timetable.tm.get(c)) {
System.out.println(t.time + " " +t.room + " "+ t.day);
}
};
Here's the code that populates the HashMap
static TimeTableDataModel timetable = new TimeTableDataModel();
Course course = new Course(courseCode,null,section,group);
Timings dt = new Timings(time, getDayOfWeek(i));
dt.room = roomNo;
timetable.addNewTimeTableItem(course, dt);
So to get the timings for a particular course I have to traverse the whole HashMap until the desired course Key is found. What I want is a way to distinguish between each course object contained in the HashMap Key, so I can get Timings for any random course without traversing the whole KeySet.
Thanks in advance. Please ask if somethings is unclear in code
Problem what I see here is
if(!tm.containsKey(course)){
addCourseItem(course);
}
and
if (this == obj) {
return true;
}
because you are comparing the object. Since both are same class objects equals will always return true and map concludes it as duplicate key.