Get value from HashMap<String, ArrayList<String>> - java

[updated code] (Sorry guys, I didn't provide the whole code, because in my experience large codes seem to "scare off" possible helpers.)
For an ExpandableListView I want to build a HashMap<String, ArrayList<String>> where String is the name of a category and ArrayList<String> the names of animals belonging to that category. I populate the HashMap as such:
HashMap<String, ArrayList<String>> map_groups_childs;
ArrayList<String> list_group_titles;
private void prepareListData(ArrayList<Id_triv_cat> search_results) {
list_group_titles = new ArrayList<String>(); // this is a list of group titles
map_groups_childs = new HashMap<String, ArrayList<String>>(); // this is a map. each group title gets a list of its respective childs
// temporary List of items for a certain category/group
ArrayList<String> temp_childs_list = new ArrayList<String>();
// "search_results" is an ArrayList of self defined objects each containing an ID, a name and a category name
int count = search_results.size();
int i_cat = 0;
int i=0;
// if category "i" is the same as the next category "i+1", add child to list
for (i=0; i<count-1; i++) {
// build group with category name
list_group_titles.add(search_results.get(i).get_type());
// while category does not change, add child to the temporary childs-array
while (i<=count && search_results.get(i).get_type().equals(search_results.get(i+1).get_type())) {
temp_childs_list.add(search_results.get(i).get_name());
i++;
}
// must be done, as the while loop does not get to the last "i" of every category
temp_childs_list.add(search_results.get(i).get_name());
Log.i("DEBUG", temp_childs_list.size()); // --> returns always more than 0
Log.i("DEBUG", temp_childs_list.toString()); // --> returns always something like [word1, word2, word3, ...]
Log.i("DEBUG", list_group_titles.get(i_cat)); // --> returns always a single word like "Insekten"
// add [group_title:list_of_childs] to map
map_groups_childs.put(list_group_titles.get(i_cat++), temp_childs_list);
// clear temp_list, otherwise former category's species will be added to new category
temp_childs_list.clear();
}
Log.i("DEBUG", map_groups_childs.containsKey("Insekten")); // --> returns true
Log.i("DEBUG", map_groups_childs.size()); // --> returns 10
Log.i("DEBUG", map_groups_childs.get("Insekten").size()); // --> returns 0
Log.i("DEBUG", map_groups_childs.toString()); // --> returns {Insekten=[], Reptilien=[], ...}
}
The use of the same i in the for- and while-loop may seem wrong or confusing, but it is okay. No i is skipped in any way or used twice.
All keys I put in the HashMap are there, but the ArrayList I want to get with (for example) map_groups_childs.get("Insekten") is empty. What am I doing wrong?

...
map_groups_childs.put(..., temp_childs_list);
temp_childs_list.clear();
}
Objects are passed as a reference in Java. You are always putting the same List in to the Map and clearing it after every iteration. Thus every value in the Map points to the same List which is empty.
What you probably need is something like this:
for( ... ) {
List<String> tempChildsList = new ArrayList<>();
...
mapGroupChilds.put(..., tempChildsList);
}
Thus a new List is created on every iteration.
I also agree with #CandiedOrange your code is a mess and probably overly complex. In general the point of abstractions like List and Map is to not access things by counting numerical indexes all the time.
Note that in Java, the convention is that identifiers for variables are camelCase, not under_scored.

Near as I can tell what you need are the 5 lines in the second group below
HashMap<String, ArrayList<String>> map_groups_childs = new HashMap<String, ArrayList<String>>();
ArrayList<String> list_group_titles = new ArrayList<String>();
ArrayList<String> temp_childs_list = new ArrayList<String>();
list_group_titles.add("insects");
temp_childs_list.add("Swallowtail");
temp_childs_list.add("Small White");
temp_childs_list.add("Large White");
temp_childs_list.add("Silverfish");
int k = 0;
Log.i("DEBUG", list_group_titles.get(k)); // --> returns "insects"
Log.i("DEBUG", temp_childs_list); // --> returns [Swallowtail, Small White, Large White, Silverfish]
map_groups_childs.put(list_group_titles.get(k), temp_childs_list);
Log.i("DEBUG", map_groups_childs.size()); // --> returns 1 not 10
Log.i("DEBUG", map_groups_childs.containsKey("insects")); // --> returns true
Log.i("DEBUG", map_groups_childs.get("insects").size()); // --> returns 4 not 0

Even with the edit, your code is still missing huge clues about what is going on. I've made some guesses and if I'm right, you are making this way too hard.
I've inferred the existence of a class Id_triv_cat that has getters named get_name() and get_type().
static class Id_triv_cat {
String type;
String name;
Id_triv_cat( String type, String name )
{
this.name = name;
this.type = type;
}
public String get_type()
{
return type;
}
public String get_name()
{
return name;
}
}
I've also written this code to test your prepareListData() method.
public static void main(String[] args){
ArrayList<Id_triv_cat> search_results = new ArrayList<Id_triv_cat>();
search_results.add( new Id_triv_cat("type1", "name1") );
search_results.add( new Id_triv_cat("type2", "name2") );
search_results.add( new Id_triv_cat("Insekten", "insekten name1") );
search_results.add( new Id_triv_cat("Insekten", "insekten name2") );
search_results.add( new Id_triv_cat("type3", "name3") );
new Test().myPrepareListData( search_results );
}
And while I could fix your minor defect like a good SE denizen I'm going to risk having all of this migrated to Programmers because your biggest problem is a design problem. Your code is suffering from a lack of clear local identifiers. Instead of making locals you are having a dot fest with the java utility classes that's making your code pointlessly hard to follow.
If, as I suspect, you are trying to build a map from a list of Id_triv_cat's using their type as a key and name as a value then try this:
private void myPrepareListData(ArrayList<Id_triv_cat> search_results) {
// Each group title is mapped to a list of its respective children
HashMap<String, ArrayList<String>> my_map_groups_childs =
new HashMap<String, ArrayList<String>>();
for (Id_triv_cat search_result : search_results)
{
String type = search_result.get_type();
String name = search_result.get_name();
if ( my_map_groups_childs.containsKey(type) )
{
ArrayList<String> names = my_map_groups_childs.get(type);
names.add(name);
}
else
{
ArrayList<String> names = new ArrayList<String>();
names.add(name);
my_map_groups_childs.put(type, names);
}
}
Log.i("DEBUG", "my_map_groups_childs = " + my_map_groups_childs);
}
This displays: my_map_groups_childs = {Insekten=[insekten name1, insekten name2], type1=[name1], type3=[name3], type2=[name2]}
See how a few well chosen local's can make life so much easier?
If that's not what you wanted you're going to have to make your question clearer.
And Radiodef is right. You really should use camelCase when you code in Java.

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
}

Getting a random element from a Collection

in java I would like to be able to maintain my Collection of fishes sorted by species at all time (hence the use of a HashMap) while being able to pick a random element from all species except one with constant time complexity. For example the following code does the job but with O(number of elements) complexity :
import java.util.*;
HashMap<String, ArrayList<Fish>> fishesBySpecies = new HashMap<>();
// Insert some fishes...
// Fish has a String attribute that describes its species
// Now we want to pick a random Fish that isn't from the unwantedSpecies
String unwanted = "unwanted species";
ArrayList<Fish> wantedSpecies = new ArrayList<>();
for (String species : fishesBySpecies.keySet()) {
if (!Objects.equals(species, unwanted)) {
wantedSpecies.addAll(fishesBySpecies.get(species));
}
}
// Finally !
int randomIndex = new Random().nextInt(wantedSpecies.size());
Fish randomElement = wantedSpecies.get(randomIndex);
Any idea how to do this with constant time complexity if possible ? Thanks !
What you are performing is filtering, and when filtering you have to check each element whether they need to be taken out or not. You could try to use alphabetical sorting on the keys and stop filtering once the key is alphabetically larger than your filtering (unwanted) key.
Your code can also be thoroughly shortened by using java streams:
HashMap<String, ArrayList<Fish>> fishesBySpecies = new HashMap<>();
// Insert some fishes...
// Fish has a String attribute that describes its species
// Now we want to pick a random Fish that isn't from the unwantedSpecies
String unwanted = "unwanted species";
fishesBySpecies.keySet().stream() // Get the keyset and create a stream out of it
.filter(key -> !key.equalsIgnoreCase(unwanted)) // If key is not equal to unwanted then leave it in else remove it
.forEach(filteredKey ->
wantedSpecies.addAll(fishesBySpecies.get(filteredKey))); // For each key that was left in, we fetch the fishes
OR
fishesBySpecies.keySet().stream() // Get the keyset and create a stream out of it
.forEach(key ->
{
if(!key.equalsIgnoreCase(unwanted))
{
wantedSpecies.addAll(fishesBySpecies.get(unwanted));
}
}
); // iterate and filter at the same time. Faster.
The only way I can think of would consist in maintaining an ArrayList<Fish> as well as the map you already have. There is a drawback though: adding or removing fishes would be slightly more complex:
Map<String, List<Fish>> fishesBySpecies = new HashMap<>();
List<Fish> wantedFishes = new ArrayList<>();
//...
public void addFish(String species, Fish fish) {
List<Fish> speciesFishes = fishesBySpecies.get(species);
if (speciesFishes == null) {
speciesFishes = new ArrayList<>();
fishesBySpecies.put(species, speciesFishes);
}
speciesFishes.add(fish);
// also maintain the list of wanted fishes
if (!unwantedSpecies.equals(species)) {
wantedFishes.add(fish);
}
}

LinkedHashMap with values as a vector being overwritten

When I wrote this piece of code due to the pnValue.clear(); the output I was getting was null values for the keys. So I read somewhere that adding values of one map to the other is a mere reference to the original map and one has to use the clone() method to ensure the two maps are separate. Now the issue I am facing after cloning my map is that if I have multiple values for a particular key then they are being over written. E.g. The output I am expecting from processing a goldSentence is:
{PERSON = [James Fisher],ORGANIZATION=[American League, Chicago Bulls]}
but what I get is:
{PERSON = [James Fisher],ORGANIZATION=[Chicago Bulls]}
I wonder where I am going wrong considering I am declaring my values as a Vector<String>
for(WSDSentence goldSentence : goldSentences)
{
for (WSDElement word : goldSentence.getWsdElements()){
if (word.getPN()!=null){
if (word.getPN().equals("group")){
String newPNTag = word.getPN().replace("group", "organization");
pnValue.add(word.getToken().replaceAll("_", " "));
newPNValue = (Vector<String>) pnValue.clone();
annotationMap.put(newPNTag.toUpperCase(),newPNValue);
}
else{
pnValue.add(word.getToken().replaceAll("_", " "));
newPNValue = (Vector<String>) pnValue.clone();
annotationMap.put(word.getPN().toUpperCase(),newPNValue);
}
}
sentenceAnnotationMap = (LinkedHashMap<String, Vector<String>>) annotationMap.clone();
pnValue.clear();
}
EDITED CODE
Replaced Vector with List and removed cloning. However this still doesn't solve my problem. This takes me back to square one where my output is : {PERSON=[], ORGANIZATION=[]}
for(WSDSentence goldSentence : goldSentences)
{
for (WSDElement word : goldSentence.getWsdElements()){
if (word.getPN()!=null){
if (word.getPN().equals("group")){
String newPNTag = word.getPN().replace("group", "organization");
pnValue.add(word.getToken().replaceAll("_", " "));
newPNValue = (List<String>) pnValue;
annotationMap.put(newPNTag.toUpperCase(),newPNValue);
}
else{
pnValue.add(word.getToken().replaceAll("_", " "));
newPNValue = pnValue;
annotationMap.put(word.getPN().toUpperCase(),newPNValue);
}
}
sentenceAnnotationMap = annotationMap;
}
pnValue.clear();
You're trying a bunch of stuff without really thinking through the logic behind it. There's no need to clear or clone anything, you just need to manage separate lists for separate keys. Here's the basic process for each new value:
If the map contains our key, get the list and add our value
Otherwise, create a new list, add our value, and add the list to the map
You've left out most of your variable declarations, so I won't try to show you the exact solution, but here's the general formula:
List<String> list = map.get(key); // try to get the list
if (list == null) { // list doesn't exist?
list = new ArrayList<>(); // create an empty list
map.put(key, list); // insert it into the map
}
list.add(value); // update the list

How to use a listAdapter to display Array of Json values

For an Android application that I'm building for my internship, I'm trying to display a list
of tickets from the current logged in user and display them in a ListView. Let me paste some of my code to let you see where I'm at currently:
JSONArray finalResult = finalResultObject.getJSONArray(TAG_TICKETS);
System.out.println("this is finalResult: " + finalResult);
// creating new HashMap
HashMap<String, String> map = new HashMap<String, String>();
// adding each child node to HashMap key => value
for (int i = 0; i < finalResult.length(); i++) {
JSONObject c = finalResult.getJSONObject(i);
if (c.has(TAG_CLIENT_NAME) && !c.isNull(TAG_CLIENT_NAME)) {
String clientName = c.getString(TAG_CLIENT_NAME);
map.put("client_name_map", clientName);
// System.out.println(clientName);
}
if (c.has(TAG_PROJECT_NAME) && !c.isNull(TAG_PROJECT_NAME)) {
String projectName = c.getString(TAG_PROJECT_NAME);
map.put("project_name_map", projectName);
}
// adding HashList to ArrayList
ticketList.add(map);
// System.out.println(map);
}
ListAdapter adapter = new SimpleAdapter(SecondActivity.this, ticketList, R.layout.list_item, new String[] { "client_name_map", "project_name_map" }, new int[] { R.id.client_name,
R.id.project_name });
eventualListAdapter = adapter;
I've got a couple of prints in between, left them in there to let you guys see what I'm looking at right now. My problem is that I do get the required number of items, but it repeats the same item (so it does loop through the array, but doesn't update the values). I'm currently completely new to Android, and therefore still figuring out which kind of Adapters to use etc.
In the end I'm passing the adapter to eventualListAdapter, which I created within the main thread, so I can easily call it to update the UI (I'm not sure if this is anywhere near a clean way, just trying to get things working at this point)
Thanks in advance,
Dennis
You are using the same HashMap instance for all Itens. Just move the HashMap creation to inside the loop:
// adding each child node to HashMap key => value
for (int i = 0; i < finalResult.length(); i++) {
// creating new HashMap
HashMap<String, String> map = new HashMap<String, String>();
JSONObject c = finalResult.getJSONObject(i);
if(c.has(TAG_CLIENT_NAME)&&!c.isNull(TAG_CLIENT_NAME)){
String clientName = c.getString(TAG_CLIENT_NAME);
map.put("client_name_map", clientName);
//System.out.println(clientName);
}
if(c.has(TAG_PROJECT_NAME)&&!c.isNull(TAG_PROJECT_NAME)){
String projectName = c.getString(TAG_PROJECT_NAME);
map.put("project_name_map", projectName);
}
// adding HashList to ArrayList
ticketList.add(map);
//System.out.println(map);
}

Java Parsing Using Hmap

I am new to Java. I want to Parse the data which is in this Format
Apple;Mango;Orange:1234;Orange:1244;...;
There could be more than one "Orange" at any point of time. Numbers (1,2...) increase and accordingly as the "Orange".
Okay. After splitting it, Lets assume I have stored the first two data(Apple, Orange) in a variable(in setter) to return the same in the getter function. And now I want to add the value(1234,1244....etc) in the 'orange' thing into a variable to return it later. Before that i have to check how many oranges have come. For that, i know i have to use for loop. But don't know how to store the "Value" into a variable.
Please Help me guys.
String input = "Apple;Mango;Orange:1234;Orange:1244;...;"
String values[] = input.split(";");
String value1 = values[0];
String value2 = values[1];
Hashmap< String, ArrayList<String> > map = new HashMap<String, ArrayList<String>>();
for(int i = 2; i < values.length; i = i + 2){
String key = values[i];
String id = values[i+1];
if (map.get(key) == null){
map.put(key, new ArrayList<String>());
}
map.get(key).add(id);
}
//for any key s:
// get the values of s
map.get(s); // returns a list of all values added
// get the count of s
map.get(s).size(); // return the total number of values.
Let me try to rephrase the question by how I interpreted it and -- more importantly -- how it focuses on the input and output (expectations), not the actual implementation:
I need to parse the string
"Apple;Mango;Orange:1234;Orange:1244;...;"
in a way so I can retrieve the values associated (numbers after ':') with the fruits:
I should receive an empty list for both the Apple and Mango in the example, because they have no value;
I should receive a list of 1234, 1244 for Orange.
Of course your intuition of HashMap is right on the spot, but someone may always present a better solution if you don't get too involved with the specifics.
There are a few white spots left:
Should the fruits without values have a default value given?
Should the fruits without values be in the map at all?
How input errors should be handled?
How duplicate values should be handled?
Given this context, we can start writing code:
import java.util.*;
public class FruitMarker {
public static void main(String[] args) {
String input = "Apple;Mango;Orange:1234;Orange:1244";
// replace with parameter processing from 'args'
// avoid direct implementations in variable definitions
// also observe the naming referring to the function of the variable
Map<String, Collection<Integer>> fruitIds = new HashMap<String, Collection<Integer>>();
// iterate through items by splitting
for (String item : input.split(";")) {
String[] fruitAndId = item.split(":"); // this will return the same item in an array, if separator is not found
String fruitName = fruitAndId[0];
boolean hasValue = fruitAndId.length > 1;
Collection<Integer> values = fruitIds.get(fruitName);
// if we are accessing the key for the first time, we have to set its value
if (values == null) {
values = new ArrayList<Integer>(); // here I can use concrete implementation
fruitIds.put(fruitName, values); // be sure to put it back in the map
}
if (hasValue) {
int fruitValue = Integer.parseInt(fruitAndId[1]);
values.add(fruitValue);
}
}
// display the entries in table iteratively
for (Map.Entry<String, Collection<Integer>> entry : fruitIds.entrySet()) {
System.out.println(entry.getKey() + " => " + entry.getValue());
}
}
}
If you execute this code, you will get the following output:
Mango => []
Apple => []
Orange => [1234, 1244]

Categories

Resources