Java HashMap content seems to change without changing it - java

I have a problem concerning a HashMap in Java. To explain the problem in a detailed way, i will first post some code you can refer to.
public void BLASTroute(String args[]) throws IOException, InterruptedException{
...
correctMapping CM = new correctMapping();
CM.correctMapping(RB.BLASTresults, exists);
CalculateNewConsensusSequence CNCS =
new CalculateNewConsensusSequence();
char[] consensus = CNCS.calculateNewConsensusSequence(
CM.newSeq, CM.remindGaps, EMBLreaderReference.sequence, exists);
HashMap<Integer, ArrayList<String>> gapsFused =
new HashMap<Integer, ArrayList<String>>();
for (Integer i : CM.remindGaps.keySet()) {
ArrayList<String> newList = CM.remindGaps.get(i);
gapsFused.put(i, newList);
}
GenerateGeneLists GGL = new GenerateGeneLists(
EMBLreaderReference, CM.newSeq, gapsFused, exists,
GQList, allMappedPositions);
System.out.println(CM.remindGaps.hashCode());
gapsFused=GGL.generateGeneListSNP(gapsFused);
System.out.println(CM.remindGaps.hashCode());
System.out.println(gapsFused.hashCode());
GGL.generateGeneListFrameShift(gapsFused);
}
The following occurs:
in my class correctMapping, i fill a global variable called remindGaps. I use it later in some functions, and nothing happens/everything works as expected.
Then, i make a copy of the HashMap called gapsFused (i don't know if this has something to do with my problem).
Now comes the interesting part: In the class GenerateGeneLists, i don't do a thing with the remindGaps HashMap.
However, after the function generateGeneListSNP is performed, remindGaps changed! I'll post the code for you as well, so that you can help me better:
public GenerateGeneLists(EMBL_reader EMBLreaderReference,
HashMap<String,ArrayList<String>> newSeq,
HashMap<Integer,ArrayList<String>> gapsFused, File exists,
ArrayList<GeneQualifier> GQlist,
HashMap<Integer,Integer> allMappedPositions)
throws InterruptedException{
this.EMBLreaderReference=EMBLreaderReference;
this.newSeq=newSeq;
//this.gapsFused=gapsFused;
this.exists=exists;
this.GQlist=GQlist;
this.allMappedPositions=allMappedPositions;
for (GeneQualifier GQ : this.GQlist){
startlist.add(GQ.start);
stoplist.add(GQ.stop);
startMap.put(GQ.start,GQ);
}
}
public HashMap<Integer,ArrayList<String>> generateGeneListSNP(
HashMap<Integer,ArrayList<String>> gapsFused)
throws IOException{
File GQSNP = new File (exists+"/GQsnp.txt");
BufferedWriter SNP = new BufferedWriter(new FileWriter(GQSNP));
SNP.write("#Gene_start\tGene_stop\tlocus_tag\tproduct" +
"\tputative_SNP_positions(putative_changes)\n");
HashMap<GeneQualifier,ArrayList<Integer>> GQreminder =
new HashMap<GeneQualifier,ArrayList<Integer>>();
for (String s : newSeq.keySet()){
ArrayList<String> blub = newSeq.get(s);
char[] qrySeq = blub.get(0).toCharArray();
char[] refSeq = blub.get(1).toCharArray();
int start = Integer.valueOf(blub.get(2));
int stop = Integer.valueOf(blub.get(3));
for (int i=0;i<refSeq.length;i++){
if (qrySeq[i]!=refSeq[i]&&qrySeq[i]!='-'&&qrySeq[i]!='.'){
if (mismatchList.containsKey(start+i)){
ArrayList<Character> blah = mismatchList.get(start+i);
blah.add(qrySeq[i]);
mismatchList.put(start+i, blah);
}
else {
ArrayList<Character> blah = new ArrayList<Character>();
blah.add(qrySeq[i]);
mismatchList.put(start+i,blah);
}
}
else if (qrySeq[i]!=refSeq[i]&&(qrySeq[i]=='-'||qrySeq[i]=='.')){
if (!gapsFused.containsKey(start+i)){
ArrayList<String> qwer = new ArrayList<String>();
qwer.add(String.valueOf(qrySeq[i]));
gapsFused.put(start+i,qwer);
}
else {
ArrayList<String> qwer = gapsFused.get(start+i);
qwer.add(String.valueOf(qrySeq[i]));
gapsFused.put(start+i,qwer);
}
if (!deletionPositionsAndCount.containsKey((start+i))){
int count = 1;
deletionPositionsAndCount.put(start+i, count);
}
else {
int count = deletionPositionsAndCount.get(start+i);
count = count+1;
deletionPositionsAndCount.put(start+i, count);
}
}
}
}
for (Integer a : mismatchList.keySet()){
for (int i=0;i<startlist.size();i++){
int start = startlist.get(i);
int stop = stoplist.get(i);
if (a>=start && a<=stop){
GeneQualifier GQ = startMap.get(start);
if (!GQreminder.containsKey(GQ)){
ArrayList save = new ArrayList<Integer>();
save.add(a);
GQreminder.put(GQ,save);
}
else {
ArrayList save = GQreminder.get(GQ);
save.add(a);
GQreminder.put(GQ,save);
}
break;
}
}
}
for (GeneQualifier GQ : GQreminder.keySet()) {
ArrayList<Integer> save = GQreminder.get(GQ);
int start = GQ.start;
int stop = GQ.stop;
String locus_tag =
GQ.geneFeatures.get("locus_tag").get(0).replace("\n", "");
String product =
GQ.geneFeatures.get("product").get(0).replace("\n", "");
SNP.write(start + "\t" + stop + "\t" + locus_tag +
"\t" + product + "\t");
boolean end = false;
for (int i = 0; i < save.size(); i++) {
if (i==save.size()-1) end=true;
int posi = save.get(i);
SNP.write(posi + "(");
ArrayList<Character> mismatches = mismatchList.get(posi);
for (int j = 0; j < mismatches.size(); j++) {
char snipp = mismatches.get(j);
if (j == mismatches.size() - 1) {
SNP.write(snipp + ")");
} else {
SNP.write(snipp + ",");
}
}
if (end == false){
SNP.write(",");
}
}
SNP.write("\n");
}
SNP.close();
return gapsFused;
}
As you can see, remindGaps is not used in this class, but still it undergoes changes. Do you have an idea why this is the case?
What I tested is, whether remindGaps changes if i manually change gapsFused (the made copy of the first HashMap). This is not the case, so i don't think that the copying process went wrong (for example only points to the other HashMap or references it).
I would really appreciate your ideas and help in order to solve this problem.

You have to remember that in Java all objects are passed as reference. So, when you did:
ArrayList<String> newList = CM.remindGaps.get(i);
you basically pointed newList to the same list as contained in the remindGaps map. Now, even though you work with the gapsFused, any changes to its values effect the same underlying list in the memory - to which both remindGaps and gapsFused are pointing.
Change your copy code to the following and see if it makes a difference:
ArrayList<String> newList = new ArrayList<String>(CM.remindGaps.get(i));
By doing this, you are creating a new list that newList will be pointing to and thus the changes will be encapsulated.

Your code is very long and hard to read (mainly because it doesn't respect Java naming conventions), but my guess is that your problem comes from the fact that your copy of the map simply copies the ArrayList references from one map to another:
HashMap<Integer, ArrayList<String>> gapsFused = new HashMap<Integer, ArrayList<String>>();
for (Integer i : CM.remindGaps.keySet()) {
ArrayList<String> newList = CM.remindGaps.get(i);
gapsFused.put(i, newList);
}
In the above code, you don't create any new list. You just store the same lists in another map. If you need a new list, the code should be:
Map<Integer, List<String>> gapsFused = new HashMap<Integer, List<String>>();
for (Integer i : CM.remindGaps.keySet()) {
List<String> newList = new ArrayList<STring>(CM.remindGaps.get(i));
gapsFused.put(i, newList);
}

Without analyzing all your code:
HashMap<Integer, ArrayList<String>> gapsFused = new HashMap<Integer, ArrayList<String>>();
for (Integer i : CM.remindGaps.keySet()) {
ArrayList<String> newList = CM.remindGaps.get(i);
gapsFused.put(i, newList);
}
After this code gapFused will contain entries that are copies of the entries of remindGaps, therefore those entries will reference the same objects (key and values). So if you add or remove entries in one Map it will have no effect on the other, but if you change a value accessing it through one Map you will see the change also accessing it through the other map (for example remingGaps.get(1).add("hello")).
The name "newList" used in your code is confusing because it is not a new list, just a reference on an existing one...

Since the value of the Map is an ArrayList and you are doing just a shallow copy (meaning the new Map has a reference to the same Lists as are in the first Map) and changes to the lists in the second map would be reflected in the first map. To avoid this you would need to make deep copies of the lists when you create the new Map.

Related

How to create a HashMap that would have String as key and the value would be another HashMap from one list of custom object?

I have a list of custom object,
public class Assignmentsdata {
String assignmentId;
String teacherId;
String groupName;
String sectionId;
String levelId;
String startTime;
}
ArrayList<Assignmentsdata> list = new ArrayList<>();
lets say there are 20 elements in that list.
Now I want to get the output which is a hashmap of startTime as a key and the Value would be a new HashMap of GroupID and a list of Assignments of those that had the same groupName.
OutPut Example
HashMap<startTime,HasMap<groupName,List> hashMap = new HashMap();
a little more insight about the problem: First I want to categorise based on startTime(Month) then i want to categorise based on groupName, Thanks in advance.
I have successfully categorised based on group name and created a map through below code:
for( int i = 0; i<assignmentsdataArrayList.size();i++ ){
if (hashMap.size()>0){
hashMap.get(assignmentsdataArrayList.get(i).getGroupName()).add(assignmentsdataArrayList.get(i));
}else {
hashMap.put(assignmentsdataArrayList.get(i).getGroupName(),new ArrayList<Assignmentsdata>());
hashMap.get(assignmentsdataArrayList.get(i).getGroupName()).add(assignmentsdataArrayList.get(i));
}
}
After that I am lost on how to categorise this hashmap based on the startDate and create a hashmap that would look like the above hashmap in the output heading.
your code may throw a NullPointerException at the first if branch
if (hashMap.size()>0)
{hashMap.get(assignmentsdataArrayList.get(i).getGroupName()).add(assignmentsdataArrayList.get(i));
}
the map.size()>0 doesnt means the Value of GroupName has put a new ArrayList already.
the anwser of using loop should like this
Map<String, Map<String, List<Assignmentsdata>>> map = new HashMap<>();
for (Assignmentsdata assignmentsdata : list) {
if (!map.containsKey(assignmentsdata.getStartTime())) {
map.put(assignmentsdata.getStartTime(), new HashMap<>());
}
Map<String, List<Assignmentsdata>> startTimeMap = map.get(assignmentsdata.startTime);
if (!startTimeMap.containsKey(assignmentsdata.getGroupName())) {
startTimeMap.put(assignmentsdata.getGroupName(), new ArrayList<>());
}
startTimeMap.get(assignmentsdata.groupName).add(assignmentsdata);
}
or you could use the java stream().collect(Collectors.groupingBy()) api to get the result easily
Map<String, Map<String, List<Assignmentsdata>>> result = list.stream()
.collect(Collectors.groupingBy(Assignmentsdata::getStartTime,Collectors.groupingBy(Assignmentsdata::getGroupName)));
I am answering my own question as I solved it if anyone has a better answer please passed your answer aswell, ill accept another answer suitable and efficient answer.
for( int i = 0; i<assignmentsdataArrayList.size();i++ ){
if (hashMap.size()>0){
if (hashMap.get(assignmentsdataArrayList.get(i).getGroupName())==null){
hashMap.put(assignmentsdataArrayList.get(i).getGroupName(),new ArrayList<Assignmentsdata>());
hashMap.get(assignmentsdataArrayList.get(i).getGroupName()).add(assignmentsdataArrayList.get(i));
}else{
hashMap.get(assignmentsdataArrayList.get(i).getGroupName()).add(assignmentsdataArrayList.get(i));
}
}else {
hashMap.put(assignmentsdataArrayList.get(i).getGroupName(),new ArrayList<Assignmentsdata>());
hashMap.get(assignmentsdataArrayList.get(i).getGroupName()).add(assignmentsdataArrayList.get(i));
}
}
// above part is already in the question. the second part i looped through the hashMap then the list once again, and checking if list and map entry have same group name, then made the startdate key that indexed element from the list.
HashMap<String, Map.Entry<String, ArrayList<Assignmentsdata>>> hashMapHashMap = new HashMap<>();
for (var entry : hashMap.entrySet()){
for( int j = 0; j<assignmentsdataArrayList.size();j++ ){
if (assignmentsdataArrayList.get(j).getGroupName()==entry.getKey()){
hashMapHashMap.put(assignmentsdataArrayList.get(j).getStartTime(),entry);
}
}
hashMapHashMap.put(entry.getValue().get())
}

loop through two different Arrays java

sorry if this is an stupid question or anything; but I'm having a problem looping through two different objects in java. one of them is a hashmap and the other one is an arraylist. the objects look something like this:
Map<String,Integer> myMap = new HashMap();
ArrayList<String> myArray = new ArrayList();
I need to loop through each item in "myArray" and check if the item exists in one of "myMap"'s keys.
I actually know how to do that but the way I do it, contains a lot of unnecessary looping and I want to know if there is an faster way to do it or not.
the way I do it:
Map<String,Integer> myMap = new HashMap();
ArrayList<String> myArray = new ArrayList();
for(String i:myArray) {
for(String j:myMap.keySet()) {
if(i == j) {
myMap.put(j, myMap.get(j) + 1)
}
}
}
In Java 8 and later you can use a stream (I didn't see the increment, thx jhamon for the advice):
myArray.stream().filter(myMap::containsKey).forEach(
item -> myMap.put(item, myMap.get(item) + 1)
);
You can alternativley use Map#computeIfPresent to get a one-liner like:
myArray.forEach(str -> myMap.computeIfPresent(str, (k,v) -> v+1) );
Following is the simple working example.
public class TestSample {
public static void main(String[] args) {
Map<String,Integer> myMap = new HashMap();
myMap.put("some", 0);
myMap.put("string", 0);
ArrayList<String> myArray = new ArrayList<String>(Arrays.asList("some", "some", "string", "to", "check"));
for(String i:myArray) {
int val = myMap.getOrDefault(i, -1);
if (val != -1)
myMap.put(i, val+1);
}
System.out.println(myMap);
}
}

Java Map Adding Key Values

Method :
public void itemAmountCollection() {
Map<String, List<Integer>> orderItemDetails = new LinkedHashMap<String, List<Integer>>();
ArrayList<Integer> itemsAmount = new ArrayList<Integer>();
WebElement orderItemTable = driver.findElement(By
.xpath("//*[#id='tblInfo']/tbody"));
List<WebElement> noOfItems = orderItemTable.findElements(By
.tagName("tr"));
for (int i = 1; i <= noOfItems.size(); i++) {
String itemAmount = driver.findElement(
By.xpath("//*[#id='tblInfo']/tbody/tr[" + i
+ "]/td[8]")).getText();
itemsAmount.add(Integer.parseInt(itemAmount));
orderItemDetails.put("amount", itemsAmount);
}
}
with above method we collected all the item amount with Map Collections and Output for the above method is (345,7905,345)
how can we add all the values in an particular Key (amount)
Expected Output :
8595 (i.e 345+7905+345)
I don't really get what you mean, but I'm amusing that you're trying to add all values in a List. To do this:
int result = 0;
for(int i : itemsAmount)
{
result+=1;
}
System.out.println(result);//This should print 8595.
In general Map<Key,List<Value>> structures end up needing code that looks as follows:
public addValue(Key key, Value value) {
if (!map.containsKey(key)) {
map.put(key, new ArrayList<>());
}
map.get(key).add(value);
}
In your case you should replace orderItemDetails.put with similar code.
Alternatively you could use a true Multimap from a third party library such as guava.
Summing the values would simply be:
map.get(key).stream().sum();
Assuming that the values are List which makes the stream an IntStream.

Update an ArrayList<Station> with values from a TreeMap<Integer, ArrayList<Station>>

I need to replace the outdated objects in ArrayList<Station> with the updated objects in TreeMap<Integer, ArrayList<Station>>.
TreeMap> only has a portion of the Station objects in ArrayList.
Anyone know a way to do this in a fast and smooth way? My application is time sensitive, the faster it can process it the better it is due to the big amount of data it is processing.
I can post here the code I've if it is of any help
EDIT:
Here's the code:
public ArrayList<Station> smoothPingPong(ArrayList<Station> dados){
ArrayList<Station> pingPongs = new ArrayList<>();
TreeMap<Integer, ArrayList<Station>> tmap = new TreeMap<>();
int tmap_key = 0;
for(int i = 0; i<dados.size(); i++) {
if(dados.get(i).getPingPong() == 1) {
pingPongs.add(dados.get(i));
}
}
ArrayList<Station> next = new ArrayList<Station>();
for(Station d : pingPongs) {
if(!next.isEmpty() && next.get(next.size() - 1).getId() != d.getId() - 1) {
tmap.put(tmap_key++, next);
next = new ArrayList<Station>();
}
next.add(d);
}
if(!next.isEmpty()) {
tmap.put(tmap_key++, next);
}
for(Integer a : tmap.keySet()){
//Processes the treemap updating it
}
}
Now I need to go back to the Stations in the ArrayList dados and update it with the Stations I've on the TreeMap.
Because you are not creating new Station Objects then you don't need to update anything, the station objects share the same reference in all your collections.
In this case, you are just returning a subset of the Station Collection, but the objects themselves are the same.

write data from hashmap as a CSV file

Say I have a hashmap with String type as key and ArrayList type as value, example {"value1"=["one","three","five"], "value2"=["two","four","six"]} where "value1" and "value2" are keys. I want to write the above hashmap data in following format. (so that I can read the csv file in excel)
value1,value2
one,two
three,four
five,six
My idea was to write the first key and its values as follows
value1
one
three
five
Then I was thinking of using the seek method in RandomAccessFile class to back to line 1 and again write the second key and its values. However I am not able to accomplish this task since seek function takes in the length of strings in the entire file and writes the new string after it. While I wanted the pointer to jump to the first line and append the string. Is there a better way to do this?. A quick example would be much appreciated.
Thanks
Why can't you just use 4 Strings, one for each row? Something like this:
StringBuilder keyRow = new StringBuilder();
StringBuilder value1 = new StringBuilder();
StringBuilder value2 = new StringBuilder();
StringBuilder value3 = new StringBuilder();
Iterator keys = hashmap.keySet().iterator();
boolean notFirst = true;
while(keys.hasNext()) {
String key = (String)keys.next();
ArrayList list = (ArrayList)hashmap.get(key);
if(!notFirst) {
keyRow.append(",");
value1.append(",");
value2.append(",");
value3.append(",");
}
keyRow.append(key);
value1.append((String)list.get(0));
value2.append((String)list.get(1));
value3.append((String)list.get(2));
notFirst = false;
}
Then at the end, just take the 4 Strings
String csv = keyRow.toString()+"\n"+value1.toString()+"\n"+value2.toString()+"\n"+value3.toString();
Note that this example isn't really proper CSV. Strings with commas aren't wrapped in quotes.
Or you iterate through the HashMap a thousand times if you have thousands of these rows. To save a bit of time from looking up a key, you can put them all in an ArrayList:
StringBuilder csv = new StringBuilder();
int row = 0;
ArrayList<ArrayList> list = new ArrayList<ArrayList>();
// Write the keys row:
Iterator keys = hashmap.keySet().iterator();
boolean notFirst = true;
while(keys.hasNext()) {
String key = (String)keys.next();
ArrayList tmp = (ArrayList)hashmap.get(key);
if(!notFirst) {
csv.append(",");
}
csv.append(key);
// store list
list.add(tmp);
notFirst = false;
}
csv.append("\n");
// Write the rest of the rows
while(row<numberOfTotalRow) {
notFirst = true;
for(int x=0;x<list.size();x++) {
if(!notFirst) {
csv.append(",");
}
csv.append((String)list.get(x).get(row));
notFirst = false;
}
row++;
}
You can make a method that prints out the map as you wish:
public void toString(HashMap<String, ArrayList<String>> map) {
for(int i = 0; i < map.size(); i++) {
ArrayList<String> list = new ArrayList<String>(map.keySet());
String key = list.get(i);
System.out.println(key);
for(int j = 0; j < map.get(key).size(); j++)
System.out.println(map.get(key).get(j));
}
}
The way you have imagined is impossible. A file is a continuous stream of bytes. So after you write the first value, you have this in your file : "value1\none\nthree\nfive". If you then seek to position 6 (after "value") and insert new characters, you'll be overwiting the first value's second row. The following bytes won't be magically pushed away.
The only way to do this is to traverse the data you have in a way that allows you to output the bytes in the same order that they will be in the file. So: go to each value and write the first element, to each value again and write their second element and so on.
You don't need a RandomAccessFile file, better use this:
HashMap<String, ArrayList<String>> map = new HashMap<>();
map.put("a", new ArrayList<>(Arrays.asList(new String[]{"A1", "A2", "A3"})));
map.put("b", new ArrayList<>(Arrays.asList(new String[]{"B1", "B2", "B3"})));
map.put("c", new ArrayList<>(Arrays.asList(new String[]{"C1", "C2", "C3"})));
{
/**
* Set your file printstream. For testing System.out
*/
PrintStream ps = System.out;
boolean first = true;
/**
* Size of the array list. Let's asume that the arraylist are of the
* same lenght;
*/
int s = 0;
/**
* Create a ArrayList variable because, this map class makes no guarantees
* as to the order of the map; in particular, it does not guarantee that
* the order will remain constant over time.
*/
ArrayList<Entry<String, ArrayList<String>>> l =
new ArrayList<>(map.entrySet());
for (Entry<String, ArrayList<String>> e : l) {
if (first) {
first = false;
s = e.getValue().size();
} else {
ps.print(",");
}
ps.print(e.getKey());
}
ps.println();
for (int i = 0; i < s; i++) {
first = true;
for (Entry<String, ArrayList<String>> e : l) {
if (first) {
first = false;
} else {
ps.print(",");
}
ps.print(
e.getValue().get(i));
}
ps.println();
}
}
Output:
b,c,a
B1,C1,A1
B2,C2,A2
B3,C3,A3

Categories

Resources