HashMap function 'put' not working properly - Java - java

I am reading through a CSV and saving the data to objects (an object is created for each row). The rows in the CSV are grouped by the first element (group number) - somewhere between 2-10ish rows share a group number. There are ~180 groups in the data set. To handle this data more easily, I store the data into HashMaps, where the key is the group number, and the value tied to the key is an ArrayList of the data objects.
As I iterate through the CSV's rows, I add objects to the HashMap, using the row's group number to tell where to put the new data object. If the object has a group number which has not been entered into the CSV yet, it creates a new key (its group number) and an ArrayList of data objects, containing just itself.
If the row's group number IS a key in the HashMap, it gets the ArrayList tied to the group number, adds the new data object to it, and uses the put function to re-add the new entry, with the updated ArrayList (now with one more data entry tied to the shared group number).
Code example:
ArrayList<CSVData> csvListNew = new ArrayList<CSVData>();
HashMap<Integer,ArrayList<CSVData>> CSVDataMapNew = new HashMap<Integer,ArrayList<CSVData>>();
while ((line = reader.readLine()) != null && !(line.contains(",,,,,,,,,")))
{
System.out.println(line);
String[] csvDataNew = line.split(",");
String currentGroup = csvDataNew[GroupIndex];
try {
currentGroupNumber = Integer.parseInt(currentGroup.replace("group", "").replace(" ", ""));
} catch (Exception ex) {
currentGroupNumber = previousGroupNumber;
}
String path = csvDataNew[PathIndex];
startLine = Integer.parseInt(csvDataNew[StartLineIndex]);
endLine = Integer.parseInt(csvDataNew[EndLineIndex]);
CSVData data = new CSVData(currentGroupNumber, path, startLine, endLine);
if (CSVDataMapNew.containsKey(currentGroupNumber)) { //if it does contain the current key, add the current object to the ArrayList tied to it.
csvListNew = CSVDataMapNew.get(currentGroupNumber);
csvListNew.add(clone);
CSVDataMapNew.put(currentGroupNumber, csvListNew);
} else { //if it doesnt contain the current key, make new entry
csvListNew.add(clone);
CSVDataMapNew.put(currentGroupNumber, csvListNew);
System.out.println(CSVDataMapNew.size());
System.out.println(CSVDataMapNew.get(currentGroupNumber).size());
}
csvListNew.clear(); //to make sure no excess objects are entered into the map.
previousGroupNumber = currentGroupNumber;
}
There are appropriate try-catches, etc. and the CSVDataTable is declared in its own class, being referenced statically.
The issue is, when I add in print statements at each step, its like each ArrayList within the HashMap gets erased at the end of every loop. So once the CSV is finished being iterated through, it has each key value, but the ArrayLists tied to each key are all empty. (Evidenced by looping through the HashMap afterwards).
How can I resolve this, so when I enter a value into the ArrayList and re 'put' the key and updated ArrayList into the Map, it keeps its data?

So once the CSV is finished being iterated through, it has each key
value, but the ArrayLists tied to each key are all empty. (
This
ArrayList<CSVData> csvListNew = new ArrayList<CSVData>();
should be invoked and associated to each key of your map.
But you use a single instance of the ArrayList as value for every key of your map.
And at the end of your method, you do :
csvListNew.clear();
So all your values of your map are an empty ArrayList as all refers to the same ArrayList.
To solve your problem, if the key doesn't exist in the map you should create a new ArrayList and associate it to this key :
ArrayList<CSVData> csvListNew = CSVDataMapNew.get(currentGroupNumber);
if (csvListNew == null)
csvListNew = new ArrayList<CSVData>();
CSVDataMapNew.put(csvListNew);
}
Then reuse the csvListNew variable to add the element in:
csvListNew.add(clone);
It simplifies your actual code that has undesirable duplication.

You always put the same ArrayList instance as value in your HashMap. That's the ArrayList instance created before the loop and referenced by the csvListNew variable.
This means that when you call csvListNew.clear(), you clear all the ArrayLists of your HashMap.
This can be fixed by creating a new ArrayList each time you want to put a new entry in your HashMap:
if (CSVDataMapNew.containsKey(currentGroupNumber)) {
csvListNew = CSVDataMapNew.get(currentGroupNumber);
csvListNew.add(clone);
} else {
csvListNew = new ArrayList<>(); // that's the main required fix
csvListNew.add(clone);
CSVDataMapNew.put(currentGroupNumber, csvListNew);
System.out.println(CSVDataMapNew.size());
System.out.println(CSVDataMapNew.get(currentGroupNumber).size());
}
In addition, remove the csvListNew.clear() call.

When you get a list from a hashMap you get a reference to the list. Everything you do with this list afterwards will affect the list that is in the map. This means two things:
You don't have to put the List back into the map after you added something to it
You have to create a new List for every Map entry. You currently don't do that.
This should fix it (also some adjusted code style):
Map<Integer,List<CSVData>> CSVDataMapNew = new HashMap<>();
while ((line = reader.readLine()) != null && !(line.contains(",,,,,,,,,")))
{
System.out.println(line);
String[] csvDataNew = line.split(",");
String currentGroup = csvDataNew[GroupIndex];
try {
currentGroupNumber = Integer.parseInt(currentGroup.replace("group", "").replace(" ", ""));
} catch (Exception ex) {
currentGroupNumber = previousGroupNumber;
}
String path = csvDataNew[PathIndex];
startLine = Integer.parseInt(csvDataNew[StartLineIndex]);
endLine = Integer.parseInt(csvDataNew[EndLineIndex]);
CSVData data = new CSVData(currentGroupNumber, path, startLine, endLine);
if (CSVDataMapNew.containsKey(currentGroupNumber)) {
CSVDataMapNew.get(currentGroupNumber).add(clone);
} else {
ArrayList<CSVData> csvListNew = new ArrayList<CSVData>();
CSVDataMapNew.put(currentGroupNumber, csvListNew);
csvListNew.add(clone);
}
previousGroupNumber = currentGroupNumber;
}

Related

How to retrieve a particular element from a list in java?

I am loading a list with some values for instance config key and config value.
I need to retrieve values for each config key from the list and then add those values into another list.
The problem is that my TO object returns these values one at a time because I am looping through the list and dcRaterName gets overridden every time the code loops through and the second list will have only one value but not all.
I want to add all the values into my second list.
List getDCRaterName= dasWebHandler.getDCRaterName(dasRequestTO);
Iterator itr = getDCRaterName.iterator();
while (itr.hasNext()) {
DasConfigTO dasConfigTO = (DasConfigTO) itr.next();
String dcRaterName = dasConfigTO.getConfigValue();
List<String> raterList = new ArrayList<>();
raterList.add(dcRaterName);
dasRequestTO.setSelectedRatersDes(raterList);
}
You should move creation of the raterList before the loop and set it inside dasRequestTO after the loop:
List<DasConfigTO> getDCRaterNames = dasWebHandler.getDCRaterName(dasRequestTO);
List<String> raterList = new ArrayList<>();
for (DasConfigTO dasConfigTO : getDCRaterNames) {
raterList.add(dasConfigTO.getConfigValue());
}
dasRequestTO.setSelectedRatersDes(raterList);

How can I check for duplicate values of an object in an array of objects, merge the duplicates' values, and then remove the duplicate?

Right now I have an array of "Dragon"s. Each item has two values. An ID and a Count. So my array would look something like this:
Dragon[] dragons = { new Dragon(2, 4),
new Dragon(83, 199),
new Dragon(492, 239),
new Dragon(2, 93),
new Dragon(24, 5)
};
As you can see, I have two Dragons with the ID of 2 in the array. What I would like to accomplish is, when a duplicate is found, just add the count of the duplicate to the count of the first one, and then remove the duplicate Dragon.
I've done this sort of successfully, but I would end up with a null in the middle of the array, and I don't know how to remove the null and then shuffle them.
This is what I have so far but it really doesn't work properly:
public static void dupeCheck(Dragon[] dragons) {
int end = dragons.length;
for (int i = 0; i < end; i++) {
for (int j = i + 1; j < end; j++) {
if (dragons[i] != null && dragons[j] != null) {
if (dragons[i].getId() == dragons[j].getId()) {
dragons[i] = new Item(dragons[i].getId(), dragons[i].getCount() + dragons[j].getCount());
dragons[j] = null;
end--;
j--;
}
}
}
}
}
You should most probably not maintain the dragon count for each dragon in the dragon class itself.
That aside, even if you are forced to use an array, you should create an intermeditate map to store your dragons.
Map<Integer, Dragon> idToDragon = new HashMap<>();
for (Dragon d : yourArray) {
// fetch existing dragon with that id or create one if none present
Dragon t = idToDragon.computeIfAbsent(d.getId(), i -> new Dragon(i, 0));
// add counts
t.setCount(t.getCount() + d.getCount());
// store in map
idToDragon.put(d.getId(), t);
}
Now the map contains a mapping between the dragons' ids and the dragons, with the correct counts.
To create an array out of this map, you can just
Dragon[] newArray = idToDragon.values().toArray(new Dragon[idToDragon.size()]);
You may be force to store the result in an array but that doesn't mean that you're force to always use an array
One solution could be using the Stream API, group the items adding the count and save the result into an array again. You can get an example of how to use the Stream API to sum values here. Converting a List<T> into a T[] is quite straightforward but anyways, you have an example here
The size of an array cannot be changed after it's created.
So you need to return either a new array or list containing the merged dragons.
public static Dragon[] merge(Dragon[] dragonArr) {
return Arrays.stream(dragonArr)
// 1. obtain a map of dragon IDs and their combined counts
.collect(groupingBy(Dragon::getId, summingInt(Dragon::getCount)))
// 2. transform the map entries to dragons
.entrySet().stream().map(entry -> new Dragon(entry.getKey(), entry.getValue()))
// 3. collect the result as an array
.toArray(Dragon[]::new);
}

Update HashMap without adding new entry

My program uses two HashmMap and they have exactly the same number of entries and the same keys.
One (tableMap) is static and never changes. The other one is dynamic (partitionMap), this means that I need to update values.
My algorithm got a problem, because seems to be adding one more entry when it is supposed to be not.
//I have a LinkedList of strings that I want to add to the HashMap partitionMap
LinkedList<String> partition = new LinkedList<String>();
for (TerminalNode terminalNode : ctx.U()) {
partition.add(terminalNode.getText());
}
//for each entry of tableMap
for(Entry<String, LinkedList<String>> entry : tableMap.entrySet())
{
//I retrieve keys and values from tableMap
String key = entry.getKey();
LinkedList<String> attributes = entry.getValue();
//the condition: if my linkedlist is included in the other do...
if(attributes.containsAll(partition))
{
//get the list of values
ArrayList<LinkedList<String>> l = partitionMap.get(key);
//but the first time is always null since I init partitionMap without values
if(l==null)
{
ArrayList<LinkedList<String>> firstLL = new ArrayList<LinkedList<String>>();
firstLL.add(partition);
partitionMap.put(key, firstLL); //BUG HERE! add one more entry instead of just updating values
}
else
{
l.add(partition);
partitionMap.put(key, l);
}
}
}
Does anybody have an idea why this is wrong?

Why this map.put() is overwriting the existing values event though map.containskey() returns false?

There might be a silly mistake that I am unable to note, but in the code below whenever I put a new key,value pair, it replaces all other values for existing keys as well. I did a check to see if it already contains the key, but the check map.containsKey() always returns false as it should, then what is wrong here?
while ((line=reader.readLine())!=null){
String[] DZs;
if (id%2==0){
String[] values=line.split(" ");
String[] low=values[0].replace("[","").replace("]","").split(",");//lower limit array of subs
String[] high=values[1].replace("[", "").replace("]","").split(",");//upper limit of subs
assert low.length==high.length;
int[] lowdim=new int[low.length];
int[] highdim=new int[high.length];
for(int i=0;i<low.length;i++){
lowdim[i]=Integer.parseInt(low[i].trim());
highdim[i]=Integer.parseInt(high[i].trim());
}
lowerBound=lowdim;
upperBound=highdim;
id++;
}
else{
id++;
DZs=line.split(" ");
if (!subDzs.isEmpty()){
subDzs.clear();
}
for(String dz:DZs){
subDzs.add(dz);
}
Participant sub=new Participant(lowerBound,upperBound);
allSubs.add(sub);
System.out.println("Map contains key? " +subToDz.containsKey(sub));//returns false
subToDz.put(sub,subDzs);//overwrites existing values everytime new key,value is put
}
}
I'm not sure what subDzs is. Probably a List. Anyway, you are using the same value object for all your put statements. Each time you call subDzs.clear(), you are clearing the values of all the entries in your map. You should assign a new instance (of whatever type it is) to subDzs before putting it in the Map.
Replace
if (!subDzs.isEmpty()){
subDzs.clear();
}
for(String dz:DZs){
subDzs.add(dz);
}
Participant sub=new Participant(lowerBound,upperBound);
allSubs.add(sub);
System.out.println("Map contains key? " +subToDz.containsKey(sub));//returns false
subToDz.put(sub,subDzs);//overwrites existing values everytime new key,value is put
with
subDzs = new ... // create a new instance
for(String dz:DZs){
subDzs.add(dz);
}
Participant sub=new Participant(lowerBound,upperBound);
allSubs.add(sub);
System.out.println("Map contains key? " +subToDz.containsKey(sub));
subToDz.put(sub,subDzs);

Placing a new value to a existing key in a hashtable

I am using a hashmap to populate a jtable. The user selects a row(s) and clicks a edit button. I am taking the value from the hashmap and placing it in a textarea. The user can make changes and then clicks another button. I have the new value and the key, but I am not sure how to write the changed value back to the right key in the hashmap.
THis is where I am writing the data out to the textarea
private void outputSelection() {
StringBuffer csb = new StringBuffer();
String s = "";
int[] row = selectTable.getSelectedRows();
for(int i = row.length-1; i >= 0; i--){
String check = (String) EdiMapTableModel.getMapInstance().getValueAt(i, EdiMapTableModel.getMapInstance().COMMENT_COL);
if (!isNullOrEmpty(check)) {
if (csb.length() > 0) {
csb.append("\n");
}
csb.append(check);
}
}
s = csb.toString();
csb.setLength(0);
output.append(s);
}
This is where I am trying to put the value back
private void inputSelection() {
String s = output.getText();
int[] row = selectTable.getSelectedRows();
for(int i = row.length-1; i >= 0; i--){
TCComponentItemRevision check = (TCComponentItemRevision) EdiMapTableModel.getMapInstance().getValueAt(i, EdiMapTableModel.getMapInstance().ITEMID_COL);
EdiMapTableModel.getMapInstance().commentBackMap(check, s);
repaint();
}
}
This is where I am trying to put it back in the map
public void commentBackMap(int row, TCComponentItemRevision id, String comment) {
if(model.containsKey(id)) {
model.put(id, comment);
}
fireTableDataChanged();
}// end commentBackMap()
I know containsKey is not right above. id is the key value
Do I need to iterate through the hashmap looking for a match? Don't know if it matters but it is a linkedhashmap instead of a hashmap
According to the HashMap#put documentation:
Associates the specified value with the specified key in this map. If
the map previously contained a mapping for the key, the old value is
replaced.
So all you have to do is call put with the same key and the new value, it will do the replacement for you.
This also applies to LinkedHashMap because it inherits the put method from HashMap.
If you want to maintain the location of your map entry, you can't put it again since that's going to move it to the end of the LinkedHashMap. You'll need a holder object, say an Object[1], where you'll replace its member without putting the map entry again.
Or maybe reevaluate your choice of LinkedHashMap.

Categories