Problem
I am trying to solve a problem, where we are given an app name, service name, and version number of an api. We need to print the name of app, which is using the latest version for most of the service.
FOR EX -
Mail App, Authentication API, v6
Video Call App, Authentication API, v7
Mail App, Data Storage API, v10
Chat App, Data Storage API, v11
Mail App, Search API, v6
Chat App, Authentication API, v8
Chat App, Presence API, v2
Video Call App, Data Storage API, v11
Video Call App, Video Compression API, v3
Answer – Chat App
Here chat app is using the latest version for Data Storage API, Authentication API & Presence API [As Presence API is used in only one app so its version in itself is the latest]
I have pasted the code below that I have written to solve this problem. How can we solve it better?
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class Main {
static Map<String, Integer> map = new HashMap<String, Integer>();
static Map<String, List<VersionDetails>> versionDetails = new HashMap<String, List<VersionDetails>>();
static Map<String, Integer> rank = new HashMap<String, Integer>();
public static void main(String[] args) throws IOException {
String inputPath = "input.txt";
String outputPath = "output.txt";
readFile(inputPath);
String appName = sortVersionDetails();
BufferedWriter writer = new BufferedWriter(new FileWriter(outputPath, true));
writer.append(appName);
writer.close();
}
private static String sortVersionDetails() {
Integer max_rank = Integer.MIN_VALUE;
String answer = "";
for (Map.Entry<String, List<VersionDetails>> li : versionDetails.entrySet()) {
String appName = li.getKey();
for (VersionDetails vd : li.getValue()) {
if (vd.version < map.get(vd.apiName)) {
Integer value = rank.get(li.getKey()) - 1;
rank.put(appName, value);
}
}
if (max_rank < rank.get(appName)) {
answer = appName;
max_rank = rank.get(appName);
}
}
return answer;
}
private static void readFile(String path) throws IOException {
File file = new File(path);
try {
BufferedReader br = new BufferedReader(new FileReader(file));
String st;
while ((st = br.readLine()) != null) {
String arr[] = st.split(",");
String apiName = arr[1];
String applicationName = arr[0];
Integer version = Integer.valueOf((arr[2].trim()).substring(1));
if (!map.containsKey(apiName)) {
map.put(apiName, version);
rank.put(applicationName, 1);
} else {
Integer value = map.get(apiName) > version ? map.get(apiName) : Integer.valueOf(version);
map.put(apiName, value);
}
VersionDetails vd = new VersionDetails(apiName, version);
if (!versionDetails.containsKey(applicationName)) {
List<VersionDetails> li = new LinkedList<VersionDetails>();
li.add(vd);
versionDetails.put(applicationName, li);
} else {
versionDetails.get(applicationName).add(vd);
}
}
br.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
static class VersionDetails {
String apiName;
Integer version;
public VersionDetails(String apiName, Integer version) {
// TODO Auto-generated constructor stub
this.version = version;
this.apiName = apiName;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "[ apiName ] = " + apiName + " ; [ version ] = " + version;
}
}
}
Solution
What you have there is essentially CSV data with three columns. If you added a third field for app name to VersionDetails
you could wasily use a CSV reader to read the file and not bother yourself with the mundane task of reading files and parsing text into DAOs.
There are two separate responsibilities in your code: reading the file and processing the entries. You should separate those into separate classes. Your file reader could provide a Stream<VersionDetails>
and your entry processor could be a Consumer<VersionDetails>
that is passed to Stream.forEach(...)
.
Regarding the algorithm, you don’t give any reason why the complete result set should be stored in a Map
between reading and processing. You might as well just look at the version number and only keep a reference to the VersionDetails
with the greatest version number. If you separate the responsibilities like I suggest above, this improvement becomes quite natural.