Improving code design to enhance performance - java

I am trying to do something like below. I don't like the design of this as I am using 4 for loops to achieve this. Can I further enhance the design to achieve this?
Creating a map with dates as keys.
Sort the list values inside the map on dates(dates have hours and minutes here)
Giving an incremental id to each dto.
int serialNumber = 1;
if (hList != null && !hList.isEmpty()) {
// create a Map with dates as keys
HashMap<String, ArrayList<BookDTO>> mapObj = new HashMap<>();
for (int count = 0; count < hList.size(); count++) {
BookDTO bookDTO = (BookDTO) hList.get(count);
ArrayList<BookDTO> list = new ArrayList<>();
list.add(bookDTO);
Calendar depDate = bookDTO.getDepartureDate();
SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy");
if (depDate != null) {
String formattedDate = format.format(depDate.getTime());
if (mapObj.containsKey(formattedDate)) {
mapObj.get(formattedDate).add(bookDTO);
} else {
mapObj.put(formattedDate, list);
}
}
}
// Sort the values inside the map based on dates
for (Entry<String, ArrayList<BookingDTO>> entry : mapObj.entrySet()) {
Collections.sort(entry.getValue(), new BookDTOComparator(DATES));
}
for (Entry<String, ArrayList<BookDTO>> entry : mapObj.entrySet()) {
serialNumber = setItinerarySerialNumber(entry.getValue(), serialNumber);
}

I believe you can merge two last loops. So, we will see only two loops. (for now I can see three loops)
You can also try Arrays.parallelSort(entry.getValue()) if lists are too large and it is applicable
Also, if it applicable, see code below:
int serialNumber = 1;
SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy");
ArrayList<BookDTO> hListCopy = new ArrayList<>(hList);
Collections.sort(hListCopy, new NewBookDTOComparator()); // 2. sorting
HashMap<String, ArrayList<BookDTO>> mapObj = new HashMap<>();
for (BookDTO bookDTO : hListCopy) {
serialNumber = setItinerarySerialNumber(bookDTO, serialNumber); // 3. serialNumber
Calendar depDate = bookDTO.getDepartureDate();
if (depDate != null) {
String formattedDate = format.format(depDate.getTime());
if (mapObj.containsKey(formattedDate)) {
mapObj.get(formattedDate).add(bookDTO);
} else {
ArrayList<BookDTO> list = new ArrayList<>();
list.add(bookDTO);
mapObj.put(formattedDate, list);
}
}
}
So, only one loop (and one sorting algorithm).
For list cope-constructor used System.arraycopy internally, you can google performance of.
You can sort hList instead without creating new 'hListCopy' if applicable.
Beware of NewBookDTOComparator, you should sort not only by minutes and hours, but also by 'DepartureDate'
I think SimpleDateFormat format should be static field or class field.
You can also try Arrays.parallelSort(hListCopy) if lists are too large and it is applicable

Related

How to go through HashMap and compare the object's data and replace the value for HashMap?

I am having a hard time writing a couple of lines of code. All my current codes are in: https://github.com/anabeen/MeetingJava
The problem that I have is, finding a suitable way to go through the hashmap and get the meetings that have overlapping times and replace them.
Map<LocalDate, Set<Meeting>> meetings = new HashMap<LocalDate, Set<Meeting>>();
Let's say we have a HashMap of
[{2011-03-21=[objectMeeting1, objectMeeting2]}]
and we need to add objectMeeting3 to that hashMap. How do I select the key "2011-03-21" to look at the objects in that hashmap and compare the set of objects in there with a new objectMeeting3's time (part of the data from object) and then replace that object?
In GitHub, I am trying to pass the MeetingSchedulerTest (). This is where I am stuck at:
Meeting meeting = extractMeeting(employeeId, requestLines[i],
officeStartTime, officeFinishTime, meetingSlotRequest1);
if(meetings.containsKey(meetingDate)){
// if the order overlaps
for (Map.Entry<LocalDate, Set<Meeting>> meetingEntry : meetings.entrySet()) {
if (meetingDate == meetingEntry.getKey())
{
Set<Meeting> setOfMeeting = meetingEntry.getValue();
for (Meeting m : setOfMeeting) {
}
}
}
// if the order doesn't
if (meetings.get(meetingDate) != null)
//shouldNotHaveOverlappingMeetings
{
System.out.println("HERES?");
meetings.remove(meetingDate);
Set<Meeting> meetingsForDay = new HashSet<Meeting>();
meetingsForDay.add(meeting);
meetings.put(meetingDate, meetingsForDay);
} else
{
System.out.println("HERES2?");
meetings.get(meetingDate).add(meeting);
}
}else if (meeting != null){
// if meeting doens't have meetingDate then create a new HashMap with date & Meeting
System.out.println("HERES3?");
Set<Meeting> meetingsForDay = new HashSet<Meeting>();
meetingsForDay.add(meeting);
meetings.put(meetingDate, meetingsForDay);
}
}
I figured out the answer by using this:
for (Map.Entry<LocalDate, Set<Meeting>> meetingEntry : meetings.entrySet()) {
if (meetingDate.equals(meetingEntry.getKey()))
{
System.out.println("HERES1? ");
Set<Meeting> setOfMeeting = meetingEntry.getValue();
for (Meeting m : setOfMeeting) {
System.out.println("comparing time? " + m.getStartTime().getHourOfDay() + " TO "
+ meeting.getStartTime().getHourOfDay());
if (m.compareTo(meeting) == 0) {
continue;
} else {
setToPut.add(m);
}
}
}
}

Apache Flink: Wierd FlatMap behaviour

I'm ingesting a stream of data into Flink. For each 'instance' of this data, I have a timestamp. I can detect if the machine I'm getting the data from is 'producing' or 'not producing', this is done via a custom flat map function that's located in it's own static class.
I want to calculate how long the machine has been producing / not producing.
My current approach is collecting the production and non production timestamps in two plain lists. For each 'instance' of the data, I calculate the current production/non-production duration by subtracting the latest timestamp from the earliest timestamp. This is giving me incorrect results, though. When the production state changes from producing to non producing, I clear the timestamp list for producing and vice versa, so that if the production starts again, the duration starts from zero.
I've looked into the two lists I collect the respective timestamps in and I see things I don't understand. My assumption is that, as long as the machine 'produces', the first timestamp in the production timestamp list stays the same, while new timestamps are added to the list per new instance of data.
Apparantly, this assumption is wrong since I get seemingly random timestamps in the lists. They are still correctly ordered, though.
Here's my code for the flatmap function:
public static class ImaginePaperDataConverterRich extends RichFlatMapFunction<ImaginePaperData, String> {
private static final long serialVersionUID = 4736981447434827392L;
private transient ValueState<ProductionState> stateOfProduction;
SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss.SS");
DateFormat timeDiffFormat = new SimpleDateFormat("dd HH:mm:ss.SS");
String timeDiffString = "00 00:00:00.000";
List<String> productionTimestamps = new ArrayList<>();
List<String> nonProductionTimestamps = new ArrayList<>();
public String calcProductionTime(List<String> timestamps) {
if (!timestamps.isEmpty()) {
try {
Date firstDate = dateFormat.parse(timestamps.get(0));
Date lastDate = dateFormat.parse(timestamps.get(timestamps.size()-1));
long timeDiff = lastDate.getTime() - firstDate.getTime();
if (timeDiff < 0) {
System.out.println("Something weird happened. Maybe EOF.");
return timeDiffString;
}
timeDiffString = String.format("%02d %02d:%02d:%02d.%02d",
TimeUnit.MILLISECONDS.toDays(timeDiff),
TimeUnit.MILLISECONDS.toHours(timeDiff) % TimeUnit.HOURS.toHours(1),
TimeUnit.MILLISECONDS.toMinutes(timeDiff) % TimeUnit.HOURS.toMinutes(1),
TimeUnit.MILLISECONDS.toSeconds(timeDiff) % TimeUnit.MINUTES.toSeconds(1),
TimeUnit.MILLISECONDS.toMillis(timeDiff) % TimeUnit.SECONDS.toMillis(1));
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println("State duration: " + timeDiffString);
}
return timeDiffString;
}
#Override
public void open(Configuration config) {
ValueStateDescriptor<ProductionState> descriptor = new ValueStateDescriptor<>(
"stateOfProduction",
TypeInformation.of(new TypeHint<ProductionState>() {}),
ProductionState.NOT_PRODUCING);
stateOfProduction = getRuntimeContext().getState(descriptor);
}
#Override
public void flatMap(ImaginePaperData ImaginePaperData, Collector<String> output) throws Exception {
List<String> warnings = new ArrayList<>();
JSONObject jObject = new JSONObject();
String productionTime = "0";
String nonProductionTime = "0";
// Data analysis
if (stateOfProduction == null || stateOfProduction.value() == ProductionState.NOT_PRODUCING && ImaginePaperData.actSpeedCl > 60.0) {
stateOfProduction.update(ProductionState.PRODUCING);
} else if (stateOfProduction.value() == ProductionState.PRODUCING && ImaginePaperData.actSpeedCl < 60.0) {
stateOfProduction.update(ProductionState.NOT_PRODUCING);
}
if(stateOfProduction.value() == ProductionState.PRODUCING) {
if (!nonProductionTimestamps.isEmpty()) {
System.out.println("Production has started again, non production timestamps cleared");
nonProductionTimestamps.clear();
}
productionTimestamps.add(ImaginePaperData.timestamp);
System.out.println(productionTimestamps);
productionTime = calcProductionTime(productionTimestamps);
} else {
if(!productionTimestamps.isEmpty()) {
System.out.println("Production has stopped, production timestamps cleared");
productionTimestamps.clear();
}
nonProductionTimestamps.add(ImaginePaperData.timestamp);
warnings.add("Production has stopped.");
System.out.println(nonProductionTimestamps);
//System.out.println("Production stopped");
nonProductionTime = calcProductionTime(nonProductionTimestamps);
}
// The rest is just JSON stuff
Do I maybe have to hold these two timestamp lists in a ListState?
EDIT: Because another user asked, here is the data I'm getting.
{'szenario': 'machine01', 'timestamp': '31.10.2018 09:18:39.432069', 'data': {1: 100.0, 2: 100.0, 101: 94.0, 102: 120.0, 103: 65.0}}
The behaviour I expect is that my flink program collects the timestamps in the two lists productionTimestamps and nonProductionTimestamps. Then I want my calcProductionTime method to subtract the last timestamp in the list from the first timestamp, to get the duration between when I first detected the machine is "producing" / "not-producing" and the time it stopped "producing" / "not-producing".
I found out that the reason for the 'seemingly random' timestamps is Apache Flink's parallel execution. When the parallelism is set to > 1, the order of events isn't guaranteed anymore.
My quick fix was to set the parallelism of my program to 1, this guarantees the order of events, as far as I know.

RandomForest with Weka in Java

I am working on a project and I need some examples how to implement RandomForest in Java with weka? I did it with IBk(), it worked. If I do it with RandomForest in the same way, it does not work.
Does anyone have a simple example for me how to implement RandomForest and how to get probability for each class (i did it with IBk withclassifier.distributionForInstance(instance) Function and it returned me probabilities for each class). How can I do it for RandomForest? I will need to get probability of every tree and to combine it?
//example
ConverrterUtils.DataSource source = new ConverterUtils.DataSource ("..../edit.arff);
Instances dataset = source.getDataSet();
dataset.setClassIndex(dataset.numAttributes() - 1);
IBk classifier = new IBk(5); classifier.buildClassifier(dataset);
Instance instance = new SparseInstance(2);
instance.setValue(0, 65) //example data
instance.setValue(1, 120); //example data
double[] prediction = classifier.distributionForInstance(instance);
//now I get the probability for the first class
System.out.println("Prediction for the first class is: "+prediction[0]);
You can calculate the the infogain while buidling the Model in the RandomForest. It is much slower and requires alot of memory while buidling model. I am not so sure about the documentation. you can add options or setValues while buiilding the model.
//numFolds in number of crossvalidations usually between 1-10
//br is your bufferReader
Instances trainData = new Instances(br);
trainData.setClassIndex(trainData.numAttributes() - 1);
RandomForest rf = new RandomForest();
rf.setNumTrees(50);
//You can set the options here
String[] options = new String[2];
options[0] = "-R";
rf.setOptions(options);
rf.buildClassifier(trainData);
weka.filters.supervised.attribute.AttributeSelection as = new weka.filters.supervised.attribute.AttributeSelection();
Ranker ranker = new Ranker();
InfoGainAttributeEval infoGainAttrEval = new InfoGainAttributeEval();
as.setEvaluator(infoGainAttrEval);
as.setSearch(ranker);
as.setInputFormat(trainData);
trainData = Filter.useFilter(trainData, as);
Evaluation evaluation = new Evaluation(trainData);
evaluation.crossValidateModel(rf, trainData, numFolds, new Random(1));
// Using HashMap to store the infogain values of the attributes
int count = 0;
Map<String, Double> infogainscores = new HashMap<String, Double>();
for (int i = 0; i < trainData.numAttributes(); i++) {
String t_attr = trainData.attribute(i).name();
//System.out.println(i+trainData.attribute(i).name());
double infogain = infoGainAttrEval.evaluateAttribute(i);
if(infogain != 0){
//System.out.println(t_attr + "= "+ infogain);
infogainscores.put(t_attr, infogain);
count = count+1;
}
}
//iterating over the hashmap
Iterator it = infogainscores.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pair = (Map.Entry)it.next();
System.out.println(pair.getKey()+" = "+pair.getValue());
System.out.println(pair.getKey()+" = "+pair.getValue());
it.remove(); // avoids a ConcurrentModificationException
}

MongoDB update with layered JSON - how to efficiently? Dot notation?

Mongodb has an update function, where it can increment pre-existing fields. However, I found that it could only update flat JSON. Whenever there's a JSONObject inside of a JSONObject, with a value I want to increment, I can't actually seem to do it. It will return this error:
com.mongodb.WriteConcernException: Write failed with error code 14 and error message
'Cannot increment with non-numeric argument: {laneQty: { BOTTOM: 1 }}'
As you can see, I tried update incrementing laneQty.BOTTOM by 1. I don't want to write an algorithm to change every single layered json field into dot notation(like laneQty.BOTTOM), so is there a way to either turn the JSON into dot notation pre-upsert?
For now my general upsert function looks like this:
public boolean incrementJson(BasicDBObject json, String colName, ArrayList<String> queryParams, ArrayList<String> removeParams){
/*make sure the game id AND the main player id can't both be the same.
If either/or, it's fine. We don't want duplicates.
*/
BasicDBObject query = new BasicDBObject();
DBCollection collection = db.getCollection(colName);
for(int i = 0; i < queryParams.size(); i++){
String param = queryParams.get(i);
query.put(param, json.get(param));
}
for(String param : removeParams){
json.remove(param);
}
return collection.update(query, new BasicDBObject("$inc", json), true, false).isUpdateOfExisting();
}
Is there any suggested upgrades to this code that could make it easily update layered json as well? Thank you!
By the way, it'll be very hard for me to hardcode this. There are a ton of layered objects and that would take me forever. Also, I am not in complete control of which fields are populated in the layers, so I can't just say laneQty.BOTTOM every single time because it will not always exist. Prior to upserting, the BasicDBObject json was actually a java bean parsed into BasicDBObject. This is its constructor if it's of any help:
public ChampionBean(int rank, int division, int assists, int deaths, int kills, int qty, int championId,
HashMap<String, Integer> laneQty, HashMap<String, Integer> roleQty,
ParticipantTimelineDataBean assistedLaneDeathsPerMinDeltas,
ParticipantTimelineDataBean assistedLaneKillsPerMinDeltas, ParticipantTimelineDataBean creepsPerMinDeltas,
ParticipantTimelineDataBean csDiffPerMinDeltas, ParticipantTimelineDataBean damageTakenDiffPerMinDeltas,
ParticipantTimelineDataBean damageTakenPerMinDeltas, ParticipantTimelineDataBean goldPerMinDeltas,
ParticipantTimelineDataBean xpDiffPerMinDeltas, ParticipantTimelineDataBean xpPerMinDeltas, int wins,
int weekDate, int yearDate) {
super();
this.rank = rank;
this.division = division;
this.assists = assists;
this.deaths = deaths;
this.kills = kills;
this.qty = qty;
this.championId = championId;
this.laneQty = laneQty;
this.roleQty = roleQty;
this.assistedLaneDeathsPerMinDeltas = assistedLaneDeathsPerMinDeltas;
this.assistedLaneKillsPerMinDeltas = assistedLaneKillsPerMinDeltas;
this.creepsPerMinDeltas = creepsPerMinDeltas;
this.csDiffPerMinDeltas = csDiffPerMinDeltas;
this.damageTakenDiffPerMinDeltas = damageTakenDiffPerMinDeltas;
this.damageTakenPerMinDeltas = damageTakenPerMinDeltas;
this.goldPerMinDeltas = goldPerMinDeltas;
this.xpDiffPerMinDeltas = xpDiffPerMinDeltas;
this.xpPerMinDeltas = xpPerMinDeltas;
this.wins = wins;
this.weekDate = weekDate;
this.yearDate = yearDate;
}
The participantTimelineDataBean is another bean with 4 int fields inside of it. I want to increment those fields (so yes it's only 2 layers deep, so if there's a solution with 2 layers deep availability I'll take that too).
Use the dot-notation:
new BasicDBObject("$inc", new BasicDBObject("laneQty.BOTTOM", 1) )
Alternative quick&dirty solution: Just collection.save the whole document under the same _id.
Use this library:
https://github.com/rhalff/dot-object
For example if you have an object like this:
var jsonObject = {
info : {
firstName : 'aamir',
lastName : 'ryu'
email : 'aamiryu#gmail.com'
},
}
then your node.js code would be like this:
var dot = require('dot-object');
var jsonObject = // as above ;-);
var convertJsonObjectToDot = dot.dot(jsonObject);
console.log(convertJsonObjectToDot);
Output will be as shown below:
{
info.firstName : 'aamir',
info.lastName : 'ryu',
info.email : 'aamiryu#gmail.com
}
Please bear with me, this is my first answer on stackoverflow ever, since i was searching for the same thing and i found one solution to it, hope it helps you out.

Populating a HashMap with an Array

So I tried finding a tutorial on how to do this but nothing gets this complicated. This is the first I am learning of HAshMaps so I am sure my solution should be easy, but I don't know how to do it.
I am trying to use an Array to populate a HashMap, and when I run the program my print out shows up null, which indicates that it isn't populating for me. Been working on this for two days, and am really lost and confused.
I am trying to get my key "expenses" to be valued with a "type".
Edit: I would like my case two to be a printout of
1: groceries
2: Entertainment
3: Etc.....
public static void main(String[] args) throws FileNotFoundException, IOException
{
// TODO code application logic here
// HashMap<String, String> map = new HashMap<String, String>();
HashMap<String, List<Expenses>> map = new HashMap<>();
List <Expenses> expenseType = new ArrayList();
double amount, totalAmount;
int cmd, year, month, date;
String type, resp;
totalAmount = 0;
String fname = JOptionPane.showInputDialog("Enter the name of the budget file, none if no file");
if (fname.compareTo("none") !=0)
{
FileInputStream ist = new FileInputStream(fname);
ObjectInputStream ifile = new ObjectInputStream(ist);
}
boolean done = false;
while(!done)
{
resp = JOptionPane.showInputDialog("Enter a command from: \n"
+ "\t1:Add a new deduction\n" //think its done
+ "\t2:Add a new expense\n" //this is done, but could be made better wit
+ "\t3:Add a deposit\n" //This is done
+ "\t4:Deduction options\n"
+ "\t5:Expense Options\n"
+ "\t6:Total balances in bank\n"
+ "\t7:quit");
cmd = Integer.parseInt(resp);
switch(cmd)
{
case 1:
break;
case 2:
//Give the option to add new spending occurence.
//Give option to choose from array of spending types.
resp = JOptionPane.showInputDialog("Enter a command from: \n"
+ "\t1: Create a new expense\n" //done
+ "\t2: Choose from expense list\n"
+ "\t3:quit");
int cmd2 = Integer.parseInt(resp);
switch (cmd2)
{
case 1:
type = JOptionPane.showInputDialog("Enter the type of the expense:");
resp = JOptionPane.showInputDialog("Enter the amount of the expense:");
amount = Double.parseDouble(resp);
resp = JOptionPane.showInputDialog("Enter the year of the expense:");
year = Integer.parseInt(resp);
resp = JOptionPane.showInputDialog("Enter the month of the expense:");
month = Integer.parseInt(resp);
resp = JOptionPane.showInputDialog("Enter the date of the expense:");
date = Integer.parseInt(resp);
// List<Expenses> expenses = map.get(type);
// Does the map have a List for type?
if (expenseType == null) {
// No. Add one.
expenseType = new ArrayList<>();
map.put(type, expenseType);
}
Expenses e = new Expenses(type, amount, year, month, date);
expenseType.add(e);
// map.put(type, new ArrayList(expenses));
map.put(type, expenseType);
break;
case 2:
//Use a hashmap to search through the ArrayLIst and print out options.
//How do I populate the HashMap?
type = null;
List<Expenses> typelist = map.get(type); //reads from map
System.out.println(typelist);
break;
}
}
}
}
}
Please don't use raw types. And, if I understand you, then you want something like
Map<String, List<Expenses>> map = new HashMap<>();
Then, to add to the List in the Map - use something like
List<Expenses> expenses = map.get(type);
// Does the map have a List for type?
if (expenses == null) {
// No. Add one.
expenses = new ArrayList<>();
map.put(type, expenses);
}
Expenses e = new Expenses(type, amount, year, month, date);
expenses.add(e);
1) You should have this line
map.put(new String(type),expenses);
instead of
map.put(expenses, new String(type));
to get value from hashmap using key i.e. type.
2) Also remove double quotes from
List<Expenses> typelist = map.get("type");
to pass variable type.

Categories

Resources