Use of an ArrayList of HashMaps - java

Could someone tell me what is wrong with the code below? When I run the code below the first line after the try { produces the following error :
"java.lang.IndexOutOfBoundsException: Index: 0, Size: 0"
public static class ASIFFile {
private ArrayList<HashMap<String,String>> data;
private static int currRec = 0; //assign each record a numeric id based on this figure.
// Method for reading ADIFfile
public ArrayList<HashMap<String,String>> ReadASIFfile (File DataFile) {
data = new ArrayList<HashMap<String, String>>(500);
try {
HashMap<String, String> temp = new HashMap<String,String>(10);
data.set(currRec, temp);
(data.get(currRec)).put("recID", Integer.toString(currRec));//give the record a numeric ID
...

You never add anything to your ArrayList. You have an ArrayList that can hold HashMaps, but is currently empty.You have to add a new HashMap to the index you want to use before you use that index.

As Kevin mentioned, you never put anything in your ArrayList. Your code should look something like this...
data = new ArrayList<HashMap<String, String>>(500);
try {
//give the record a numeric ID
data.add(currRec, temp);
In your previous solution, you're calling data.set(currRec, temp); which assumes there is a HashMap element in position currRec of your ArrayList. If you haven't put anything in the ArrayList yet, there will be no element currRec.

Related

Calling a method on an object that is in an ArrayList

I've been stuck on this question in an assignment in which I must "List the stations along a given subway line"
There are two Hash Maps:
private Map<String, Station> allStations = new HashMap<String, Station>(); // all stations, indexed by station name
private Map<String, SubwayLine> allSubwayLines = new HashMap<String, SubwayLine>(); // all subway lines, indexed by name of the line
I am trying to call the "getStations()" method, which is a part of the subwayLine class:
public List<Station> getStations(){
return Collections.unmodifiableList(stations);
}
On a subwayLine object which is linked to a button:
public void listStationsOnLine(){
UI.clearText();
List<SubwayLine> subwayLines = new ArrayList(allSubwayLines.values());
for(SubwayLine s : subwayLines){
s.getStations();
}
}
However, this does nothing. Is there anyway in which I can return the stations along the given subwayLine?
you have to save your data in an arraylist again :
public void listStationsOnLine(){
UI.clearText();
List<SubwayLine> subwayLines = new ArrayList(allSubwayLines.values());
List<Collection<Station>> stations = new ArrayList();
for(SubwayLine s : subwayLines){
stations.add(s.getStations());
}
}
The void keyword in your method means that it does not return anything.
You might want to replace it with List and return it at the end of your method, like
public List<Station> listStationsOnLine(){
UI.clearText();
List<SubwayLine> subwayLines = new ArrayList(allSubwayLines.values());
List<Collection<Station>> stations = new ArrayList<>();
for(SubwayLine s : subwayLines){
stations.add(s.getStations());
}
return stations
}
Please note that you loop through all subwaylines and therefore your stations arraylist consists of all stations that are along some subwayline. So not a list of stations per subwayline. Also, I added a diamond operator to the arraylist call. That will automatically make an ArrayList of the type Collection<Station>. However, making a List of Collections seems not right here? You probably just want a List of <Station>.
Moreover, the call s.getStations() seems to yield all stations at subwayLine s? The description of your question seems to ask how to implement thát method.
In your question, you seem to prefer a list of stations for a given subwayline. That should then be input for your method, something like this:
public List<Station> listStationsOnLine(SubwayLine subwayline){
UI.clearText();
List<Station> allStations = getStations();
List<Station> stations = new ArrayList<>();
for(Station s : allStations){
if(s.onLine(subwayline)) {
stations.add(s);
}
}
return stations
}

How to remove the selected item from Arraylist<HashMap<String, String>> in android

I am using Arraylist < HashMap< String, String >> in ListView to archive multi-column(I just need two column, so I use HashMap). But when I am using remove method in context menu. It would always remove the last item in the list.
The code:
#Override
public boolean onContextItemSelected(MenuItem item) {
final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
switch (item.getItemId()) {
case R.id.bc_contextmenu_delete:
list.remove(info.position);
adapter.notifyDataSetChanged();
return true;
default:
return super.onContextItemSelected(item);
}
}
What should I do to solve this problem to remove the selected one from the list?
Besides, I would also like to get those two values from the HashMap which in the ArrayList. Which code should I use here.
Here is an ArrayList:
PS4 4000<br>
PS5 5000<br>
XBOX 6000<br>
I would like to get PS4 and 4000.
Thanks all.
As per your requirement, you can create a bean for same. i.e. DummyBean. it has two field like
class DummyBean{
String name;
String val;
--getter setter method
}
Use it into List<DummyBean>.
In future if new column added than it is easy to add and expandable.
No need to wrap the HashMap into an ArrayList. HashMap itself is enough. If you want to remain the order, you should use LinkedHashMap.
A side effect is that you cannot access elements by index, so you have to iterate over it to get the last item or the one by index.
So if you don't care about duplicates I would use ArrayList with as template a Pair or a custom Object. (Where I prefer a custom object to be more readable)
ArrayList<Pair<String,String>> consoles = new ArrayList<Pair<String,int>>();
consoles.Add(Pair.create("PS4", 4000));
consoles.Add(Pair.create("PS5 ", 5000));
consoles.Add(Pair.create("XBOX ", 6000));
And remove using index:
consoles.Remove(index);
To store and retrieve your values from the Hashmap in ArrayList, You need to store the the HashMap values with keys to identify them
As with your example ,
PS4 4000
PS5 5000
XBOX 6000
ArrayList<HashMap<String ,String>> list = new ArrayList<>();
// to add item
HashMap<String ,String> val = new HashMap<>();
val.put("GAME", "PS4");
val.put("NUMBER", "4000");
list.add(val); //added to 0th index position
val = new HashMap<>();
val.put("GAME", "PS5");
val.put("NUMBER", "5000");
list.add(val); //added to 1st
// to retrieve ps4 and 400
String forPS4 = list.get(0).get("GAME");
String for4000 = list.get(0).get("4000");

How can I add a string one at a time to a HashMap<Integer, List<String>>?

This function loops through a dictionary (allWords) and uses the
getKey function to generate a key. wordListMap is a HashMap> so I need to loop through and put the key and and a List. If there is not a list I put one if there is I just need to append the next dictionary word. This is where I need help. I just can't figure out the syntax to simply append the next word to the list that is already there. Any Help would be appreciated.
public static void constructWordListMap() {
wordListMap = new HashMap<>();
for (String w : allWords) {
int key = getKey(w);
if (isValidWord(w) && !wordListMap.containsKey(key)) {
List list = new ArrayList();
list.add(w);
wordListMap.put(key, list);
} else if (isValidWord(w) && wordListMap.containsKey(key)) {
wordListMap.put(key, wordListMap.get(key).add(w));
}
}
}
map.get(key).add(value)
Simple as that.
So I've gathered that you want to, given HashMap<Integer, List<String>>, you'd like to:
create a List object
add String objects to said List
add that List object as a value to be paired with a previously generated key (type Integer)
To do so, you'd want to first generate the key
Integer myKey = getKey(w);
Then, you'd enter a loop and add to a List object
List<String> myList = new List<String>;
for(int i = 0; i < intendedListLength; i++) {
String myEntry = //wherever you get your string from
myList.add(myEntry);
}
Lastly, you'd add the List to the HashMap
myHash.put(myKey, myList);
Leave any questions in the comments.
else if (isValidWord(w) && wordListMap.containsKey(key)) {
wordListMap.put(key, wordListMap.get(key).add(w));
}
If you want to add a new value to your list, you need to retrieve that list first. In the code above, you are putting the return value of add into the table (which is a boolean), and that is not what you want.
Instead, you will want to do as Paul said:
else if (isValidWord(w) && wordListMap.containsKey(key)) {
wordListMap.get(key).add(w);
}
The reason this works is because you already added an ArrayList to the table earlier. Here, you are getting that ArrayList, and adding a new value to it.

Writing a method with ArrayList of strings as parameters

I am trying to write a method that takes an ArrayList of Strings as a parameter and that places a string of four asterisks in front of every string of length 4.
However, in my code, I am getting an error in the way I constructed my method.
Here is my mark length class
import java.util.ArrayList;
public class Marklength {
void marklength4(ArrayList <String> themarklength){
for(String n : themarklength){
if(n.length() ==4){
themarklength.add("****");
}
}
System.out.println(themarklength);
}
}
And the following is my main class:
import java.util.ArrayList;
public class MarklengthTestDrive {
public static void main(String[] args){
ArrayList <String> words = new ArrayList<String>();
words.add("Kane");
words.add("Cane");
words.add("Fame");
words.add("Dame");
words.add("Lame");
words.add("Same");
Marklength ish = new Marklength();
ish.marklength4(words);
}
}
Essentially in this case, it should run so it adds an arraylist with a string of "****" placed before every previous element of the array list because the lengths of the strings are all 4.
BTW
This consists of adding another element
I am not sure where I went wrong. Possibly in my for loop?
I got the following error:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at Marklength.marklength4(Marklength.java:7)
at MarklengthTestDrive.main(MarklengthTestDrive.java:18)
Thank you very much. Help is appreciated.
Let's think about this piece of code, and pretend like you don't get that exception:
import java.util.ArrayList;
public class Marklength {
void marklength4(ArrayList <String> themarklength){
for(String n : themarklength){
if(n.length() ==4){
themarklength.add("****");
}
}
System.out.println(themarklength);
}
}
Ok, so what happens if your list just contains item.
You hit the line if(n.length() ==4){, which is true because you are looking at item, so you go execute its block.
Next you hit the line themarklength.add("****");. Your list now has the element **** at the end of it.
The loop continues, and you get the next item in the list, which happens to be the one you just added, ****.
The next line you hit is if(n.length() ==4){. This is true, so you execute its block.
You go to the line themarklength.add("****");, and add **** to the end of the list.
Do we see a bad pattern here? Yes, yes we do.
The Java runtime environment also knows that this is bad, which is why it prevents something called Concurrent Modification. In your case, this means you cannot modify a list while you are iterating over it, which is what that for loop does.
My best guess as to what you are trying to do is something like this:
import java.util.ArrayList;
public class Marklength {
ArrayList<String> marklength4(ArrayList <String> themarklength){
ArrayList<String> markedStrings = new ArrayList<String>(themarklength.size());
for(String n : themarklength){
if(n.length() ==4){
markedStrings.add("****");
}
markedStrings.add(n);
}
System.out.println(themarklength);
return markedStrings;
}
}
And then:
import java.util.ArrayList;
public class MarklengthTestDrive {
public static void main(String[] args){
ArrayList <String> words = new ArrayList<String>();
words.add("Kane");
words.add("Cane");
words.add("Fame");
words.add("Dame");
words.add("Lame");
words.add("Same");
Marklength ish = new Marklength();
words = ish.marklength4(words);
}
}
This...
if(n.length() ==4){
themarklength.add("****");
}
Is simply trying to add "****" to the end of the list. This fails because the Iterator used by the for-each loop won't allow changes to occur to the underlying List while it's been iterated.
You could create a copy of the List first...
List<String> values = new ArrayList<String>(themarklength);
Or convert it to an array of String
String[] values = themarklength.toArray(new String[themarklength.size()]);
And uses these as you iteration points...
for (String value : values) {
Next, you need to be able to insert a new element into the ArrayList at a specific point. To do this, you will need to know the original index of the value you are working with...
if (value.length() == 4) {
int index = themarklength.indexOf(value);
And then add a new value at the required location...
themarklength.add(index, "****");
This will add the "****" at the index point, pushing all the other entries down
Updated
As has, correctly, been pointed out to me, the use of themarklength.indexOf(value) won't take into account the use case where the themarklength list contains two elements of the same value, which would return the wrong index.
I also wasn't focusing on performance as a major requirement for the providing a possible solution.
Updated...
As pointed out by JohnGarnder and AnthonyAccioly, you could use for-loop instead of a for-each which would allow you to dispense with the themarklength.indexOf(value)
This will remove the risk of duplicate values messing up the index location and improve the overall performance, as you don't need to create a second iterator...
// This assumes you're using the ArrayList as the copy...
for (int index = 0; index < themarklength.size(); index++) {
String value = themarklength.get(index);
if (value.length() == 4) {
themarklength.add(index, "****");
index++;
But which you use is up to you...
The problem is that in your method, you didn't modify each string in the arraylist, but only adds 4 stars to the list. So the correct way to do this is, you need to modify each element of the arraylist and replace the old string with the new one:
void marklength4(ArrayList<String> themarklength){
int index = 0;
for(String n : themarklength){
if(n.length() ==4){
n = "****" + n;
}
themarklength.set(index++, n);
}
System.out.println(themarklength);
}
If this is not what you want but you want to add a new string "**" before each element in the arraylist, then you can use listIterator method in the ArrayList to add new additional element before EACH string if the length is 4.
ListIterator<String> it = themarklength.listIterator();
while(it.hasNext()) {
String name = it.next();
if(name.length() == 4) {
it.previous();
it.add("****");
it.next();
}
}
The difference is: ListIterator allows you to modify the list when iterating through it and also allows you to go backward in the list.
I would use a ListIterator instead of a for each, listiterator.add likely do exactly what you want.
public void marklength4(List<String> themarklength){
final ListIterator<String> lit =
themarklength.listIterator(themarklength.size());
boolean shouldInsert = false;
while(lit.hasPrevious()) {
if (shouldInsert) {
lit.add("****");
lit.previous();
shouldInsert = false;
}
final String n = lit.previous();
shouldInsert = (n.length() == 4);
}
if (shouldInsert) {
lit.add("****");
}
}
Working example
Oh I remember this lovely error from the good old days. The problem is that your ArrayList isn't completely populated by the time the array element is to be accessed. Think of it, you create the object and then immediately start looping it. The object hence, has to populate itself with the values as the loop is going to be running.
The simple way to solve this is to pre-populate your ArrayList.
public class MarklengthTestDrive {
public static void main(String[] args){
ArrayList <String> words = new ArrayList<String>() {{
words.add("Kane");
words.add("Cane");
words.add("Fame");
words.add("Dame");
words.add("Lame");
words.add("Same");
}};
}
}
Do tell me if that fixes it. You can also use a static initializer.
make temporary arraylist, modify this list and copy its content at the end to the original list
import java.util.ArrayList;
public class MarkLength {
void marklength4(ArrayList <String> themarklength){
ArrayList<String> temp = new ArrayList<String>();
for(String n : themarklength){
if(n.length() ==4){
temp.add(n);
temp.add("****");
}
}
themarklength.clear();
themarklength.addAll(temp);
System.out.println(themarklength);
}
}

How to optimize the updating of values in an ArrayList<Integer>

I want to store all values of a certain variable in a dataset and the frequency for each of these values. To do so, I use an ArrayList<String> to store the values and an ArrayList<Integer> to store the frequencies (since I can't use int). The number of different values is unknown, that's why I use ArrayList and not Array.
Example (simplified) dataset:
a,b,c,d,b,d,a,c,b
The ArrayList<String> with values looks like: {a,b,c,d} and the ArrayList<Integer> with frequencies looks like: {2,3,2,2}.
To fill these ArrayLists I iterate over each record in the dataset, using the following code.
public void addObservation(String obs){
if(values.size() == 0){// first value
values.add(obs);
frequencies.add(new Integer(1));
return;//added
}else{
for(int i = 0; i<values.size();i++){
if(values.get(i).equals(obs)){
frequencies.set(i, new Integer((int)frequencies.get(i)+1));
return;//added
}
}
// only gets here if value of obs is not found
values.add(obs);
frequencies.add(new Integer(1));
}
}
However, since the datasets I will use this for can be very big, I want to optimize my code, and using frequencies.set(i, new Integer((int)frequencies.get(i)+1)); does not seem very efficient.
That brings me to my question; how can I optimize the updating of the Integer values in the ArrayList?
Use a HashMap<String,Integer>
Create the HashMap like so
HashMap<String,Integer> hm = new HashMap<String,Integer>();
Then your addObservation method will look like
public void addObservation(String obs) {
if( hm.contains(obs) )
hm.put( obs, hm.get(obs)+1 );
else
hm.put( obs, 1 );
}
I would use a HashMap or a Hashtable as tskzzy suggested. Depending on your needs I would also create an object that has the name, count as well as other metadata that you might need.
So the code would be something like:
Hashtable<String, FrequencyStatistics> statHash = new Hashtable<String, FrequencyStatistics>();
for (String value : values) {
if (statHash.get(value) == null) {
FrequencyStatistics newStat = new FrequencyStatistics(value);
statHash.set(value, newStat);
} else {
statHash.get(value).incrementCount();
}
}
Now, your FrequencyStatistics objects constructor would automatically set its inital count to 1, while the incrementCound() method would increment the count, and perform any other statistical calculations that you might require. This should also be more extensible in the future than storing a hash of the String with only its corresponding Integer.

Categories

Resources