Nithin Chandy Nithin Chandy - 2 months ago 20
Java Question

LinkedHashSet inside a LinkedHashMap

I have a JSON response of the following format which I will be parsing to get the keys (key_as_String) and values (Expected_Usage and Actual_Usage) for each key.

"aggregations": {
"Inner_aggregation": {
"doc_count": 366,
"Hours_aggregation": {
"doc_count": 366,
"by_day": {
"buckets": [
{
"key_as_string": "2016-01-11",
"key": 1452556800000,
"doc_count": 1,
"Expected_Usage": {
"value": 5
},
"Actual_Usage": {
"value": 3
}
},
{
"key_as_string": "2016-01-12",
"key": 1452556800000,
"doc_count": 1,
"Expected_Usage": {
"value": 43
},
"Actual_Usage": {
"value": 2
}
},
.........,
.........
]
}
}
}
}
}


I want to keep the insertion order as the KEYS returned by Elastic Search is already sorted. I also want to maintain the order of VALUES for each key.

Thinking of using LinkedHashmap and a LinkedHashSet for this.

LinkedHashMap<String, LinkedHashSet<Integer>> LinkedMap =
new LinkedHashMap<String,LinkedHashSet<Integer>>();

LinkedHashSet<Integer> LinkedSet =
new LinkedHashSet<Integer>();
LinkedSet.add(3);
LinkedSet.add(4);
LinkedSet.add(2);

LinkedMap.put("2016/03/11",LinkedSet);


for(Map.Entry m:LinkedMap.entrySet()){
System.out.println("Key is : " + m.getKey() + " Values: " + m.getValue());
}


Are there any better alternatives in terms of memory and performance?

Answer

There are many choices, mainly determined by how you are going to consume the acquired data - e.g. if you don't care about the calling code modifying your data, designing structures with public members will result in the least CPU pressure.

The maximum possible key values in the response is 365 and there'll be only 2 values for each Key.

I see. The I recommend dropping the LinkedHashSet and writing a custom class to hold those two integers unboxed (dont waste CPU in converting int to Integer and back):

public class ExpectedVsActual {
  // if you don't care too much of your data integrity
  // along other lines of coding, make those public
  // and forget about getters
  protected int expected;
  protected int actual;

  public ExpectedVsActual(int exp, int act) {
    this.expected=exp;
    this.actual=act;
  }
  public int getExpected() {
    return this.expected;
  }
  public int getActual() {
     return this.actual;
  }
}

Then

LinkedHashMap<String, ArrayList<ExpectedVsActual>> myMap=...; // etc

Of course, if you don;t need to search through the keys, then you dont need a map.

If you want individual entries for each key, maybe it's better to wrap each entry as a structure:

public class MyEntryRepresentation {
   protected String dateStr;

   // ArrayList: faster iteration by position
   // LinkedList: memory conservative - doesn't allocate more than necessary
   protected List<ExpectedVsActual> data;

   public MyEntryRepresentation(String date) 
     this.dateStr=date;
     this.data=new ArrayList<ExpectedVsActual>();
   }

   public void addEntry(int expected, int actual) {
     this.data.add(new ExpectedVsActual(expected, actual));
   }

   public List<ExpectedVsActual> getValues() {
     // if you don't care what the caller will do with your List 
     return this.data;
     // If you want to forbid them to modify the returned list
     // return Collections.unmodifiableList(this.data);
   }

   public String getDateStr() {
     return this.date;
   }
} 

and then

LinkedHashMap<String, MyEntryRepresentation> map=... etc;

map.add(entry.getDateStr(), entry);