I am really new to Java and I am trying to implement something using Hashmap.
The following code is what I declared first:
private HashMap<String, TreeMap<Object, Object>> submissions = new HashMap<String, TreeMap<Object, Object>>();;
And,
public Submission add(String unikey, Date timestamp, Integer grade) {
// check the argument
if(unikey == null || timestamp == null || grade == null) {
throw new IllegalArgumentException("Null argument detected\n");
}
}
this is what I am writing at the moment. Assuming that there are items called "person", "data" and "grade". Can someone please tell me how to put them in the nested hashmap? I finished writing the getter and setter for each of the items in another class called, MySubmissions.
The Submission is an interface written in another class that contain the following methods:
public String getPerson();
public Date getTime();
public Integer getGrade();
What I want to achieve is that, for example,
?.add("aaaa1234", df.parse("2016/09/03 09:00:00"), 10);
?.add("aaaa1234", df.parse("2016/09/03 16:00:00"), 20);
?.add("cccc1234", df.parse("2016/09/03 16:00:00"), 30);
?.add("aaaa1234", df.parse("2016/09/03 18:00:00"), 40);
Thanks!
(what I exactly want to achieve is, I want to add data into the hashmap. And then using another method called, getBestGrade, I want to get the best graded person among the list but I just want to know how to store into the hashmap first using put and get...)
Create an entity
public class Submission {
private Date timestamp;
private Integer grade;
public Date getTimestamp() {
return timestamp;
}
public void setTimestamp(Date timestamp) {
this.timestamp = timestamp;
}
public Integer getGrade() {
return grade;
}
public void setGrade(Integer grade) {
this.grade = grade;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Submission that = (Submission) o;
if (timestamp != null ? !timestamp.equals(that.timestamp) : that.timestamp != null) return false;
return grade != null ? grade.equals(that.grade) : that.grade == null;
}
#Override
public int hashCode() {
int result = timestamp != null ? timestamp.hashCode() : 0;
result = 31 * result + (grade != null ? grade.hashCode() : 0);
return result;
}
}
Create a HashMap
private HashMap<String, Submission> map = new HasMap<>();
Do add
map.add("key", new Submission());
I think he wants to know how to store more than one Submission for each Person. You can do something like this:
import java.util.Date;
import java.util.HashMap;
import java.util.TreeMap;
public final class BestGrade
{
private static final HashMap<String, TreeMap<Date, Integer>> SUBMISSIONS = new HashMap<String, TreeMap<Date, Integer>>();
private BestGrade()
{}
public static void main(final String[] args)
{
// How to add
add("Person1", new Date(), Integer.valueOf(1));
add("Person1", new Date(), Integer.valueOf(10));
add("Person1", new Date(), Integer.valueOf(20));
add("Person2", new Date(), Integer.valueOf(1));
add("Person3", new Date(), Integer.valueOf(30));
add("Person3", new Date(), Integer.valueOf(40));
// How to get best grade
final Integer bestGradePerson1 = getBestGrade("Person1");
final Integer bestGradePerson3 = getBestGrade("Person2");
final Integer bestGradePerson2 = getBestGrade("Person3");
System.out.println("Bestgrade Person1: " + bestGradePerson1);
System.out.println("Bestgrade Person2: " + bestGradePerson2);
System.out.println("Bestgrade Person3: " + bestGradePerson3);
}
public static void add(final String key, final Date timestamp, final Integer grade)
{
// TODO the same for timestamp and grade
if (key == null || key.trim().isEmpty()) {
throw new IllegalArgumentException("key must not be null");
}
// Get
TreeMap<Date, Integer> submission = SUBMISSIONS.get(key);
// Create your treemap if not already exists, before adding new value to avoid NullPointerException
if (submission == null) {
submission = new TreeMap<Date, Integer>();
SUBMISSIONS.put(key, submission);
}
submission.put(timestamp, grade);
}
public static Integer getBestGrade(final String key)
{
Integer bestGrade = null;
final TreeMap<Date, Integer> submission = SUBMISSIONS.get(key);
if (submission == null) {
// When no submission available, return null or any other value you wish to show there is no best grade
return bestGrade;
}
for (final Integer grade : submission.values()) {
if (bestGrade == null) {
bestGrade = grade;
}
// Set new grade when values is higher than before
else if (bestGrade.intValue() < grade.intValue()) {
bestGrade = grade;
}
}
return bestGrade;
}
}
I'm just going to describe how to use a map of maps -- it's up to you to decide whether this is what you actually want to use. I'm going to use classes called A,B,C etc. -- you can substitute your own, including String or Submission if you like.
Make sure you have a firm understanding of a single-level Map before you tackle this -- how equals() and hashCode() are necessary for HashMap etc.
You can define a map of maps much as you have done:
Map<A, ? extends Map<B,C>> mapOfMaps;
In general, give variables a type of Map rather than HashMap or TreeMap -- you normally don't need any of the more specific methods of the implementation classes. You can always change up if you do. The ? extends Map<> part allows your map-of-maps to contains arbitrary implementations of Map.
You can instantiate this like this:
Map<A, ? extends Map<B,C>> mapOfMaps = new HashMap<>();
// or with explicit (unnecessary) type declarations:
Map<A, ? extends Map<B,C>> mapOfMaps = new HashMap<A, ? extends Map<B,C>>();
Now you have an empty map-of-maps. You can add a map to it:
Map<B,C> map = new HashMap<>();
mapOfMaps.put(new A(1), map);
Now you have a map-of-maps containing one empty map. Or you could add a map containing something:
Map<B,C> map = new HashMap<>();
map.put(b, c);
mapOfMaps.put(a, map);
Plausibly, you want to add items to a Map<B,C> when you don't know whether it exists. There's no short-cut here - you have to do:
void addToMapOfMaps(A a, B b, C c) {
Map<B,C> map = mapOfMaps.get(a);
if(map == null) {
map = new HashMap<>();
mapOfMaps.put(a,map);
}
map.put(b,c);
}
Note that this has problems if multiple threads are doing it at the same time.
Likewise if you're just reading, you have to handle missing elements at both levels:
C get(A a, B b) {
Map<B,C> map = mapOfMaps.get(a);
if(map == null) {
return null;
}
return map.get(b);
}
(Or more compactly)
C get(A a, B b) {
Map<B,C> map = mapOfMaps.get(a);
return map == null ? null : map.get(b);
}
Related
I have a list of objects that I am implementing a custom Comparator to sort them into the format as desired by me.
My object looks like the following
storageZoneId = <a long value>
floorNo = <an integer>
status = <CREATED/DRAFT>
I want my list of objects to be sorted such that they are ordered first by all DRAFT status, then by StorageZoneId and then by floorNo. What I have implemented till now does the final two steps, but how can I do the first one - basically split the list such that all "DRAFT" status are first followed by all "CREATED".
public int compare(Object o1, Object o2) {
ServiceRequest serviceRequest1 = (ServiceRequest) o1;
ServiceRequest serviceRequest2 = (ServiceRequest) o2;
int compare_zone = Long.compare(serviceRequest1.getStorageZoneId(), serviceRequest2.getStorageZoneId());
if (compare_zone == 0) {
return Integer.compare(serviceRequest1.getFloorNumer(), serviceRequest2.getFloorNumer());
} else {
return compare_zone;
}
}
Using Java 8+ you could create your comparator in another way:
//put this somewhere so you don't recreate it everytime
Map<String, Integer> statusOrder = new HashMap<>();
statusOrder.put("DRAFT", 1);
statusOrder.put("CREATED", 2);
Comparator<ServiceRequest> comp = Comparator.comparing(sr -> statusOrder.get(sr.getStatus()))
.thenComparing(ServiceRequest::getStorageZoneId)
.thenComparing(ServiceRequest::getFloorNumer);
(Note: the syntax might contain error since I wrote this off the top of my head without having access to a compiler atm)
I believe that something like so should do the trick:
public int compare(Object o1, Object o2) {
ServiceRequest serviceRequest1 = (ServiceRequest) o1;
ServiceRequest serviceRequest2 = (ServiceRequest) o2;
if (o1.getStatus().equals("DRAFT") && o2.getStatus().equals("CREATED")){
return 1;
}
if (o1.getStatus().equals("CREATED") && o1.getStatus().equals("DRAFT")){
return -1;
}
int compare_zone = Long.compare(serviceRequest1.getStorageZoneId(), serviceRequest2.getStorageZoneId());
if (compare_zone == 0) {
return Integer.compare(serviceRequest1.getFloorNumer(), serviceRequest2.getFloorNumer());
} else {
return compare_zone;
}
}
If the status of o1 is "DRAFT" and status of o2 is "CREATED" then o1 comes first for sure - return 1. In the second comparison the situation is reversed. Only if status of both is the same then other fields matter, we just continue with what you got so far.
enum Status {
DRAFT,
CREATED
}
class MyObject {
long storageZoneId;
int floorNo;
Status status;
public MyObject(long storageZoneId, int floorNo, Status status) {
this.storageZoneId = storageZoneId;
this.floorNo = floorNo;
this.status = status;
}
public long getStorageZoneId() {
return storageZoneId;
}
public int getFloorNo() {
return floorNo;
}
public Status getStatus() {
return status;
}
#Override
public String toString() {
return "MyObject{" +
"storageZoneId=" + storageZoneId +
", floorNo=" + floorNo +
", status=" + status +
'}';
}
}
public static void main(String[] args) {
List<MyObject> myObjectList = new ArrayList<>();
myObjectList.add(new MyObject(1, 5, Status.DRAFT));
myObjectList.add(new MyObject(3, 2, Status.DRAFT));
myObjectList.add(new MyObject(4, 3, Status.CREATED));
myObjectList.add(new MyObject(6, 8, Status.CREATED));
Comparator<MyObject> comparator = Comparator.comparing(MyObject::getStatus)
.thenComparing(MyObject::getStorageZoneId)
.thenComparing(MyObject::getFloorNo);
myObjectList.sort(comparator);
for (MyObject obj : myObjectList) {
System.out.println(obj);
}
}
Just to add to the other answer, you can use the Comparator builder to simplify the Comparator logic. The status field is defined as an enum, and enums are sorted based on the relative ordering in which the enum values appear in the enum definition. So your list will first be sorted based on DRAFT status as it appears first in the enum, and then CREATED status.
I have a class called LineUp, it is an ArrayList of a class called Event. An Event has three values a String Act, a Venue (it's own class), and an int Session.
An Event might be declared like this.
Event e1 = new Event("Foo Fighters", northstage, "1")
LineUp is an ArrayList, Event being elements like e1.
In my LineUp class I have to make an invariant that checks that every Event contained within the ArrayList lineup has a unique Venue and Session. Because this assignment requires that I follow specification exactly, it is irrelevant whether the combination of Act, Venue and Session is unique, to follow specification I must /only/ ensure that Venue and Session are unique.
How do I check for duplicates but only of specific values within an ArrayList?
Thank-you.
If you only need to check if there are duplicates (considering venue-session pairs), you could create a helper Pair class with only the attributes that matter in this specific case. Then map the events to Pair objects, remove the duplicates and check if the size is the same.
You could, for example, create a nested class inside LineUp:
class LineUp {
private List<Event> events = new ArrayList<>();
private static final class Pair<U, V> {
final U first;
final V second;
Pair(U first, V second) {
this.first = first;
this.second = second;
}
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Pair)) {
return false;
}
Pair<U, V> that = (Pair<U, V>) o;
return Objects.equals(this.first, that.first)
&& Objects.equals(this.second, that.second);
}
#Override
public int hashCode() {
return Objects.hash(this.first, this.second);
}
}
// rest of the LineUp class
}
Then create a method that return false if there are any duplicates:
public boolean duplicateVenueSessions() {
// Map each Event to a Pair<Venue, Integer> and remove the duplicates
long numDistinct = this.events.stream()
.map(e -> new Pair<>(e.venue, e.session))
.distinct()
.count();
// return false if the original number of events is different from the
// number of distinct events considering only venue and session values
return this.events.size() != numDistinct;
}
If can't use Java 8, you could use a Set instead:
public boolean duplicateVenueSessions() {
Set<Pair<String, Integer>> distinct = new HashSet<>();
for (Event e : this.events) {
Pair<String, Integer> venueSession = new Pair<>(e.venue, e.session);
if (distinct.contains(venueSession)) {
return true;
}
distinct.add(venueSession);
}
return false;
}
If I understand everything correctly you can use Map in a method to store values
Map<Map<Venue, Integer>, Act> lineup = new HashMap<>();
it incorporated uniqueness of Venue-Session pair.
However as Venue is your own class, you will have to implement equals() and hashCode() methods for Venue in order for this solution to work
EDIT:
what I meant wa something like this:
Map<Map<Integer, Venue>,String> uniqueMap = new HashMap<>();
for (Event event: events) { // assuming events is ArrayList
Map<Integer, Venue> sessionVenueMap = new HashMap<>();
sessionVenueMap.put(event.getSession(), event.getVenue());
//check if we stored this pair in our cool map
if (uniqueMap.get(sessionVenueMap) == null) {
//if not
//store this in our uniqieMap in our method
uniqueMap.put(sessionVenueMap, event.getAct);
sessionVenueMap.put(event.getSession(), event.getVenue);
} else {
// if map has this pair
// then it is not unique
return false;
}
venueSessionMap.put(.getVenue(); event.getSession();
}
return true;
code is not tested though, but you get the general idea, although it seems quite complex. probably there is a better solution
I have been running into this problem sometimes when programming.
Imagine I have a table of data with two columns. The first column has strings, the second column has integers.
I want to be able to store each row of the table into a dynamic array. So each element of the array needs to hold a string and an integer.
Previously, I have been accomplishing this by just splitting each column of the table into two separate ArrayLists and then when I want to add a row, I would call the add() method once on each ArrayList. To remove, I would call the remove(index) method once on each ArrayList at the same index.
But isn't there a better way? I know there are classes like HashMap but they don't allow duplicate keys. I am looking for something that allows duplicate entries.
I know that it's possible to do something like this:
ArrayList<Object[]> myArray = new ArrayList<Object[]>();
myArray.add(new Object[]{"string", 123});
I don't really want to have to cast into String and Integer every time I get an element out of the array but maybe this is the only way without creating my own? This looks more confusing to me and I'd prefer using two ArrayLists.
So is there any Java object like ArrayList where it would work like this:
ArrayList<String, Integer> myArray = new ArrayList<String, Integer>();
myArray.add("string", 123);
Just create simple POJO class to hold row data. Don't forget about equals and hashCode and prefer immutable solution (without setters):
public class Pair {
private String key;
private Integer value;
public Pair(String key, Integer value) {
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public Integer getValue() {
return value;
}
// autogenerated
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Pair)) return false;
Pair pair = (Pair) o;
if (key != null ? !key.equals(pair.key) : pair.key != null) return false;
if (value != null ? !value.equals(pair.value) : pair.value != null) return false;
return true;
}
#Override
public int hashCode() {
int result = key != null ? key.hashCode() : 0;
result = 31 * result + (value != null ? value.hashCode() : 0);
return result;
}
}
Usage:
List<Pair> list = new ArrayList<Pair>();
list.add(new Pair("string", 123));
Note: in other languages there are build-in solutions for it like case-classes and tuples in Scala.
Create a Row class that holds the data.
package com.stackoverflow;
import java.util.ArrayList;
import java.util.List;
/**
* #author maba, 2012-10-10
*/
public class Row {
private int intValue;
private String stringValue;
public Row(String stringValue, int intValue) {
this.intValue = intValue;
this.stringValue = stringValue;
}
public int getIntValue() {
return intValue;
}
public String getStringValue() {
return stringValue;
}
public static void main(String[] args) {
List<Row> rows = new ArrayList<Row>();
rows.add(new Row("string", 123));
}
}
You can create very simple object, like :
public class Row{
private String strVal;
private Integer intVal;
public Row(String s, Integer i){
strVal = s;
intVal = i;
}
//getters and setters
}
Then use it as follows :
ArrayList<Row> myArray = new ArrayList<Row>();
myArray.add(new Row("string", 123));
Map is the option if you are sure that any one value among integer or string is unique. Then you can put that unique value as a key. If it is not true for your case, creating a simple POJO is best option for you. Infact, if in future, there a chance to come more values (columns) per row then also using a POJO will be less time consuming. You can define POJO like;
public class Data {
private int intValue;
private String strValue;
public int getIntValue() {
return intValue;
}
public void setIntValue(int newInt) {
this.intValue = newInt;
}
public String getStrValue() {
return strValue;
}
public void setStrValue(String newStr) {
this.strValue = newStr;
}
And in the class you can use it like;
ArrayList<Data> dataList = new ArrayList<Data>();
Data data = new Data();
data.setIntValue(123);
data.setStrValue("string");
dataList.add(data);
You should create a class (e.g. Foo) that contains an int and a String.
Then you can create an ArrayList of Foo objects.
List<Foo> fooList = new ArrayList<Foo>();
This is called a map my friend. It is similar to a dictionary in .net
http://docs.oracle.com/javase/6/docs/api/java/util/Map.html
HashMap my be the class you are looking for assuming "string" going to different for different values. Here is documentation on HashMap
Example:
HashMap<String, Integer> tempMap = new HashMap<String, Integer>();
tempMap.put("string", 124);
If you need to add more than one value, you may create HashMap<String, ArrayList> like that.
you can use google collection library Guava there is a Map called Multimap. It is collection similar to a Map, but which may associate multiple values with a single key. If you call put(K, V) twice, with the same key but different values, the multimap contains mappings from the key to both values.
Use Map to solve this problem:
Map<String, Integer> map = new HashMap<String, Integer>();
Eg:
map.put("string", 123);
Before I post this question, I found somehow similar question posted here. But the answer was based on a String. However, I have a different situation here. I am not trying to remove String but another object called AwardYearSource. This class has an int attribute called year. So I want to remove duplicates based on the year. i.e if there is year 2010 mentioned more than once, I want to remove that AwardYearSource object. How can I do that?
The simplest way to remove elements based on a field is as follows (preserving order):
Map<Integer, AwardYearSource> map = new LinkedHashMap<>();
for (AwardYearSource ays : list) {
map.put(ays.getYear(), ays);
}
list.clear();
list.addAll(map.values());
Another way would be to override hashCode() and equals(Object obj) for your object. Since it just has one field you want to use to determine equality, this is pretty straightforward. Something like:
public boolean equals(Object obj) {
if (obj == null || !(obj instanceof AwardYearSource)) {
return false;
}
return (this.year == ((AwardYearSource)obj).year);
}
public int hashCode() {
return this.year;
}
Then you can just stick all of the objects into a Set to remove duplicates:
Set<AwardYearSource> set = new Set<AwardYearSource>();
set.add(new AwardYearSource(2011));
set.add(new AwardYearSource(2012));
set.add(new AwardYearSource(2011));
for (AwardYearSource aws : set) {
System.out.println(aws.year);
}
Fairly simply. Although something bugs me about the map versions (not that I doubt they'd work, it just seems like overkill, somehow - although this version isn't necessarily any better in that regard).
Answer is functional, and threadsafe (assuming AwardYearSource is immutable).
public static List<AwardYearSource> removeDuplicateYears(
final Collection<AwardYearSource> awards) {
final ArrayList<AwardYearSource> input = new ArrayList<AwardYearSource>(awards);
// If there's only one element (or none), guaranteed unique.
if (input.size() <= 1) {
return input;
}
final HashSet<Integer> years = new HashSet<Integer>(input.size(), 1);
final Iterator<AwardYearSource> iter = input.iterator();
while(iter.hasNext()) {
final AwardYearSource award = iter.next();
final Integer year = award.getYear();
if (years.contains(year)) {
iter.remove();
} else {
years.add(year);
}
}
return input;
}
You could use a map and store your objects with the year as a key:
Map<Integer, AwardYearSource> map = new HashMap<Integer, AwardYearSource>();
map.put(someAwardYearSource1.getYear(), someAwardYearSource1);
map.put(someAwardYearSource2.getYear(), someAwardYearSource2);
etc.
At the end the map will contain unique values by year, which you can call with the values method:
Collection<AwardYearSource> noDups = map.values();
Create a HashMap object with int as the key type and your class as the value type. Then iterate over the list and insert each element to the map using:
mymap.put(source.year, source);
Then remove all elements from the origianl list and iterate over the map and insert each element to the list.
If your AwardYearSource class overrides equals and hashcode methods (Eclipse can generate both), then you can add them to a Set. The Set will not contain any duplicates.
public class AwardYearSource
{
private final int year;
public AwardYearSource(int year)
{
this.year = year;
}
#Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + year;
return result;
}
#Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AwardYearSource other = (AwardYearSource) obj;
if (year != other.year)
return false;
return true;
}
#Override
public String toString()
{
return String.valueOf(year);
}
public static void main(String[] args)
{
Set<AwardYearSource> set = new HashSet<AwardYearSource>();
set.add(new AwardYearSource(2000));
set.add(new AwardYearSource(2000));
set.add(new AwardYearSource(2000));
set.add(new AwardYearSource(2000));
System.out.println(set);
}
}
The output is [2000]. Only one item in the set.
Set<Integer> set = new HashSet<>();
list.removeIf(i -> set.contains(i.getYear()) ? true : !set.add(i.getYear()));
This should help wherein, duplication is decided based on certain property (or combination of properties), year in this case. Hope this helps.
I have bunch of log files and I want to process them in java, but I want to sort them first so I can have more human readable results.
My Log Class :
public class Log{
//only relevant fields here
private String countryCode;
private AccessType accessType;
...etc..
}
AccessType is Enum, which has values WEB, API, OTHER.
I'd like to group Log objects by both countryCode and accessType, so that end product would be log list.
I got this working for grouping Logs into log list by countryCode like this :
public List<Log> groupByCountryCode(String countryCode) {
Map<String, List<Log>> map = new HashMap<String, List<Log>>();
for (Log log : logList) {
String key = log.getCountryCode();
if (map.get(key) == null) {
map.put(key, new ArrayList<Log>());
}
map.get(key).add(log);
}
List<Log> sortedByCountryCodeLogList = map.get(countryCode);
return sortedByCountryCodeLogList;
}
from this #Kaleb Brasee example :
Group by field name in Java
Here is what I've been trying for some time now, and really stuck now ..
public List<Log> groupByCountryCode(String countryCode) {
Map<String, Map<AccessType, List<Log>>> map = new HashMap<String, Map<AccessType, List<Log>>>();
AccessType mapKey = null;
List<Log> innerList = null;
Map<AccessType, List<Log>> innerMap = null;
// inner sort
for (Log log : logList) {
String key = log.getCountryCode();
if (map.get(key) == null) {
map.put(key, new HashMap<AccessType, List<Log>>());
innerMap = new HashMap<AccessType, List<Log>>();
}
AccessType innerMapKey = log.getAccessType();
mapKey = innerMapKey;
if (innerMap.get(innerMapKey) == null) {
innerMap.put(innerMapKey, new ArrayList<Log>());
innerList = new ArrayList<Log>();
}
innerList.add(log);
innerMap.put(innerMapKey, innerList);
map.put(key, innerMap);
map.get(key).get(log.getAccessType()).add(log);
}
List<Log> sortedByCountryCodeLogList = map.get(countryCode).get(mapKey);
return sortedByCountryCodeLogList;
}
I'm not sure I know what I'm doing anymore
Your question is confusing. You want to sort the list, but you are creating many new lists, then discarding all but one of them?
Here is a method to sort the list. Note that Collections.sort() uses a stable sort. (This means that the original order of items within a group of country code and access type is preserved.)
class MyComparator implements Comparator<Log> {
public int compare(Log a, Log b) {
if (a.getCountryCode().equals(b.getCountryCode()) {
/* Country code is the same; compare by access type. */
return a.getAccessType().ordinal() - b.getAccessType().ordinal();
} else
return a.getCountryCode().compareTo(b.getCountryCode());
}
}
Collections.sort(logList, new MyComparator());
If you really want to do what your code is currently doing, at least skip the creation of unnecessary lists:
public List<Log> getCountryAndAccess(String cc, AccessType access) {
List<Log> sublist = new ArrayList<Log>();
for (Log log : logList)
if (cc.equals(log.getCountryCode()) && (log.getAccessType() == access))
sublist.add(log);
return sublist;
}
If you're able to use it, Google's Guava library has an Ordering class that might be able to help simplify things. Something like this might work:
Ordering<Log> byCountryCode = new Ordering<Log>() {
#Override
public int compare(Log left, Log right) {
return left.getCountryCode().compareTo(right.getCountryCode());
}
};
Ordering<Log> byAccessType = new Ordering<Log>() {
#Override
public int compare(Log left, Log right) {
return left.getAccessType().compareTo(right.getAccessType());
}
};
Collections.sort(logList, byCountryCode.compound(byAccessType));
You should create the new inner map first, then add it to the outer map:
if (map.get(key) == null) {
innerMap = new HashMap<AccessType, List<Log>>();
map.put(key, innerMap);
}
and similarly for the list element. This avoids creating unnecessary map elements which will then be overwritten later.
Overall, the simplest is to use the same logic as in your first method, i.e. if the element is not present in the map, insert it, then just get it from the map:
for (Log log : logList) {
String key = log.getCountryCode();
if (map.get(key) == null) {
map.put(key, new HashMap<AccessType, List<Log>>());
}
innerMap = map.get(key);
AccessType innerMapKey = log.getAccessType();
if (innerMap.get(innerMapKey) == null) {
innerMap.put(innerMapKey, new ArrayList<Log>());
}
innerMap.get(innerMapKey).add(log);
}
Firstly, it looks like you're adding each log entry twice with the final line map.get(key).get(log.getAccessType()).add(log); inside your for loop. I think you can do without that, given the code above it.
After fixing that, to return your List<Log> you can do:
List<Log> sortedByCountryCodeLogList = new ArrayList<Log>();
for (List<Log> nextLogs : map.get(countryCode).values()) {
sortedByCountryCodeLogList.addAll(nextLogs);
}
I think that code above should flatten it down into one list, still grouped by country code and access type (not in insertion order though, since you used HashMap and not LinkedHashMap), which I think is what you want.