Single Key Multiple Accounts to Map - java

My map is declared as private Map accountByReport;
I am using the below loop to check if the reportID is in the map or not before adding the corresponding Department Account to the key. Is my loop correct and what should the type declaration be in this instance and how can I catch exceptions?
for(Entity entity : entities) {
if(accountsByReport.containsKey(entity.getReportID())) {
((List<String>)accountsByReport.get(entity.getReportID())).add(entity.getDepAccount());
} else {
accountsByReport.put(entity.getReportID(), new ArrayList<String>().add(entity.getDepAccount()));
}
}

It seems you are adding a boolean (the return ok .add()) to the map in the else.
I would use this way:
for(Entity entity : entities) {
if(accountsByReport.containsKey(entity.getReportID())) {
((List<String>)accountsByReport.get(entity.getReportID())).add(entity.getDepAccount());
} else {
List<String> list = new ArrayList<String>();
list.add(entity.getDepAccount());
accountsByReport.put(entity.getReportID(), list);
}
}
You didn't paste the Map definition, but you should use typed parameters - i.e. Map<String, List<String>> accountsByReport = new HashMap<String, List<String>>(); so you don't need to do casting after .get
In fact you pasted it.
Use: private Map<String, List> accountByReport;
Refer to http://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html

Your code is correct so far, but your Map should declare with type
The map should be declared based on the type of reportID,
//if String
private Map<String, List<String>> accountByReport;
//if int
private Map<Integer, List<String>> accountByReport;
//so on

Your loop is essentially correct. The only improvements you can make is to do a single get and store the result rather than doing both a contains and a get. You should also use generics so you don't need the cast..
List<String> list = accountsByReport.get(entity.getReportID())
if(list != null) {
list.add(entity.getDepAccount());
} else {
accountsByReport.put(entity.getReportID(), new ArrayList<String>().add(entity.getDepAccount()));
}

Related

Searching for key/value pair in Collection, return all values in that Collection if successfully matched

My last attempt was poorly explained, so recreated with hopefully a clearer explanation.
I have a Collection that has the following data structure.
LinkedHashMap<String, String> currentHashMap = new LinkedHashMap<String, String>();
currentHashMap.put("id","12345");
currentHashMap.put("firstName","John");
currentHashMap.put("lastName","Doe");
haystack.add(currentHashMap);
*repeat for roughly 250,000 more entries
I need to search the HashMaps within each list entry, check if the key exists, and if it has the corresponding value. If it does, I want to return all the values in that particular hashmap
This is the code that I'm currently using.
private TreeSet<String> searchWithinCollection(List<LinkedHashMap<String, String>> haystack, String needle, String needleKey) {
TreeSet<String> returnValueSet = new TreeSet<>();
for (LinkedHashMap<String, String> mappedData : haystack) {
System.out.println(mappedData.values());
for (Entry<String, String> specificData : mappedData.entrySet()) {
if (needleKey.equals(specificData.getKey()) && needle.equals(specificData.getValue())) {
//where I want to collect the values within the current hashmap, as it's satisfied the search criteria
//the current code would only return the current key/balue pair, even if all values were collected outside the for loop. Instead, I'd want to collect "12345", "John", "Doe" if needleKey = id and needle = 12345.
}
}
}
return returnValueSet;
}
What I'd want is if a key in the current collection equaled "id", and it's value equaled "12345", then I could returned all values within that particular collection (and to use the example above, would be "12345", "John", "Doe").
The best I can do is return the first key/value, which isn't very helpful obviously. Attempting to capture all values in mappedData outside the second for loop brought no result (either with creating a new instance of a Collection, clone, or what have you.). I also found nothing that could manually advance the pointer in the for each loop to manually capture all values in the Collection.
EDIT: If it helps, where there is a System.out.println(mappedData.values()); I can get the values I'm expecting, but only the id in the example is return if values() is accessed within the if statement.
You should not iterate over all the entries in a hashmap (currently, the loop for (Entry<String, String> specificData : mappedData.entrySet()) in your code. This defeats the purpose of maintaining a hashmap.
Instead, the inner loop should become
String potentialNeedle = mappedData.get(needleKey);
if (needle.equals(potentialNeedle))
returnValueSet.addAll(mappedData.values());
You're using the map as if it was a list of entries, which completely defeats the purpose of a map: quickly access to a value for a given key:
for (Map<String, String> mappedData : haystack) {
String value = mappedData.get(needleKey);
if (needle.equals(value)) {
returnValueSet.addAll(mappedData.values());
}
}
Maybe you should take redesign in account to improve performance. it could be smart to have a map for each field you want to search. instead of your haystack create your data-structure like that:
public class PersonDataManager {
private final Map<Integer, Map<String, String>> baseData;
private final Map<String, List<Integer>> firstNameLookupMap;
private final Map<String, List<Integer>> lastNameLookupMap;
public PersonDataManager(){
this.baseData = new HashMap<>();
this.firstNameLookupMap = new HashMap<>();
this.lastNameLookupMap = new HashMap<>();
}
public void addPerson(Integer id, String firstName, String lastName){
//try to find existing person to update:
Map<String, String> personMap = baseData.get(id);
if(personMap == null){
personMap = new HashMap<>();
baseData.put(id, personMap);
}
personMap.put("firstName", firstName);
personMap.put("lastName", lastName);
//add to lookup-maps
addLookupName(firstNameLookupMap, id, firstName);
addLookupName(lastNameLookupMap, id, lastName);
}
private static void addLookupName(Map<String, List<Integer>> nameMap, Integer id, String name){
//get existing list of the name:
List<Integer> idList = nameMap.get(name);
if(idList == null){
idList = new ArrayList<>();
}
if(!idList.contains(id)){
idList.add(id);
}
}
private List<Map<String, String>> searchByName(Map<String, List<Integer>> nameMap, String name){
List<Integer> matchingIds = nameMap.get(name);
List<Map<String, String>> result = new ArrayList<>();
if(matchingIds != null){
for(Integer id : matchingIds){
result.add(searchById(id));
}
}
return result;
}
public Map<String, String> searchById(Integer id){
return baseData.get(id);
}
public List<Map<String, String>> searchByFirstName(String name){
return searchByName(firstNameLookupMap, name);
}
public List<Map<String, String>> searchByLastName(String name){
return searchByName(lastNameLookupMap, name);
}
}
This way you can easily get a hashmap of a person using its id. if you need to search by firstname or lastname you can utilize the additional maps to get the matching person-ids. hope this helps
EDIT: Just implemented a class which does exactly what i think you might need. have fun ;)

Converting Each Value in static HashMap to String Java

I have got some troubles converting each value in my HashMap to a String.
private static HashMap<String, List<Music>> musiksammlung = new
HashMap<String, List<Music>>();
This is my constructor for the HashMap. The key represents the album, the value a list of tracks from this album.
Now I want to convert each Music object to a String without creating a new HashMap, is this
possible?
I've tried it with the Iterator scheme, for loop over the entry set and so on but nothing seems to work.
Edit://
My code for the convertmethod:
public HashMap<String, List<String>> generateFormatList() {
HashMap<String, List<String>> formatList = new HashMap<String, List<String>>();
for(String key : musiksammlung.keySet())
formatList.put(key, musiksammlung.get(key).toString());
return musiksammlung;
}
But this always results in an error "is not applicable for the Arguments (String, String) so I have no idea. Do I have to override toString()?
You're on the right path but you need to convert the existing List<Music> to a List<String> and put the List<String> into your new HashMap.
You also then want to return your newly created HashMap<String, List<String>> instead of your original one.
public HashMap<String, List<String>> generateFormatList() {
HashMap<String, List<String>> formatList = new HashMap<String, List<String>>();
for(String key : musiksammlung.keySet()) {
// Value to store in map
List<String> value = new ArrayList<String>();
// Get the List<Music>
List<Music> musicList = musiksammlung.get(key);
for (Music m: musicList) {
// Add String of each Music object to the List
value.add(m.toString);
}
// Add the value to your new map
formatList.put(key, value);
}
// Return the new map
return formatList;
}
So answer your question:
Now I want to convert each Music object to a String without creating a
new HashMap, is this possible?
You need to create a new HashMap, because it's storing different type of value: List<Music> is different from List<String>.
Also as mentioned in my previous answer, make sure you override Music.toString() so that it returns a meaningful String for you instead of the one it inherits from its parent classes, which includes at least java.lang.Object
formatList wants a List<String>, but musiksammlung.get(key).toString() returns a String (not a List<String>). Did you mean this?
HashMap<String, String> formatList = new HashMap<String, String>();
Have you tried something like this:
Iterator<String> it = musiksammlung.keySet().iterator();
while (it.hasNext()) {
List<Music> ml = musiksammlung.get(it.next());
for (Music m : ml)
System.out.println(m.toString());
}
And of course you should override the Music#toString() method with something you could use.
Try to change your HashMap like this:
private static HashMap<String, List<Object>> musiksammlung = new HashMap<String,List<Object>>();
So you can save any kind of objects in this HashMap. Also use instanceof to check the type of the object before using it.

remove similar (redundant) strings from Arraylist

I'm trying to remove similar strings from an ArrayList but I'm getting this error:
CurrentModificationException
and here is my method where I pass my original arrayList (old) and get a new list without redundant strings.
ArrayList<String> removeRed(ArrayList<String> old) throws IOException
{
ArrayList<String> newList = new ArrayList<String>();
for (int i=0; i< old.size(); i++)
{
if(newList.size() < 1)
{
newList.add(old.get(0));
} else{
for(Iterator<String> iterator = newList.iterator(); iterator.hasNext();) {
while(iterator.hasNext())
{
if(!ChopMD((String) iterator.next()).equals(ChopMD(old.get(i))))
{
newList.add(old.get(i));
Log.e("new algo", "" + old.get(i) );
}
}
}
}
}}
Note that my ChopMD() returns a particular string and it works fine.
It works fine for the first few strings, this it throws that exception. Any suggestion to resolve this issue would be appreciated it. Thanks.
If you have no problems with using the standard library (always preferable, why reinvent the wheel) try
List<String> uniques = new ArrayList<String>(new HashSet<String>(oldList));
The HashSet will only contain unique strings and the ArrayList constructor takes any Collection (including a HashSet) to build a list from.
Judging from your comments it seems like you are trying to implement an Associative Array with unique keys using an ArrayList. The better approach is to use a Map implementation like HashMap to pair IDs with their associated Strings.
Map<Integer, String> map = new HashMap<>();
map.put(1, "This string corresponds to ID=1");
map.put(3, "Donald Ducks Nephews");
map.put(7, "Is a Prime");
Then to get a value associated with an ID:
int key = someObject.getID();
String value = map.get(key);
All the Map implementations use unique keys so there is no need for you to check for redundant IDs, if you try to add a new (key,value) pair the value associated with the ID will be replaced if the map contains the key.
map.put(1, "New String");
String s = map.get(1); //s will no longer be "This string corresponds to ID=1"
If you don't want this behavior you have the choice of either subclassing one of the Map implementations to ignore .put(key, value) if the map contains key,value or delegating .put(key,value) to some other class.
Subclassing:
public class UniqueValueHashMap<K,V> extends HashMap<K, V>{
#Override
public V put(K key, V value) {
if (containsKey(key))
return null;
return super.put(key, value);
}
Delegating
public class SomeClass {
private Map<Integer, String> map = new HashMap<>();
// ...stuff this class does
public String put(int key, String value) {
if (map.containsKey(key))
return null;
return map.put(key, value);
}
// ...more stuff this class does
}
Delegation is the better approach, notice how you can change the map implementation (using maybe a TreeMap instead of HashMap) without introducing a new class where you override the .put(key,value) of TreeMap.
You can iterate much easier by this
for (String oldString : old){
for (String newString : newList){
}
}
Also you can use Set to have unique strings
Set<String> newList = new HashSet<String>();
Your error is because you are changing the list WHILE it is still iterated.

How to have a key with multiple values in a map?

I have a map like this
Map map=new HashMap();//HashMap key random order.
map.put("a",10);
map.put("a",20);
map.put("a",30);
map.put("b",10);
System.out.println("There are "+map.size()+" elements in the map.");
System.out.println("Content of Map are...");
Set s=map.entrySet();
Iterator itr=s.iterator();
while(itr.hasNext())
{
Map.Entry m=(Map.Entry)itr.next();
System.out.println(m.getKey()+"\t"+m.getValue()+"\t"+ m.hashCode());
}
Output of the above program is
There are 2 elements in the map.
Content of Map are...
b 10 104
a 30 127
Now I want that key a should have multiple values like
a 10
a 20
a 30
So that I should get all the values associated by a. Please advise how can I achieve that same thing. By nesting of collections, I want key 'a' to have all the three values.
Have you checked out Guava Multimaps ?
A collection similar to a Map, but which may associate multiple values
with a single key. If you call put(K, V) twice, with the same key but
different values, the multimap contains mappings from the key to both
values.
If you really want to use standard collections (as suggested below), you'll have to store a collection per key e.g.
map = new HashMap<String, Collection<Integer>>();
Note that the first time you enter a new key, you'll have to create the new collection (List, Set etc.) before adding the first value.
To implement what you want using the Java standard library, I would use a map like this:
Map<String, Collection<Integer>> multiValueMap = new HashMap<String, Collection<Integer>>();
Then you can add values:
multiValueMap.put("a", new ArrayList<Integer>());
multiValueMap.get("a").add(new Integer(10));
multiValueMap.get("a").add(new Integer(20));
multiValueMap.get("a").add(new Integer(30));
If this results uncomfortable for you, consider wrapping this behaviour in a dedicated Class, or using a third-party solution, as others have suggested here (Guava Multimap).
You shouldn't ignore the generic parameters. What you have is
Map<String, Integer> map = new HashMap<>();
if you want to code the solution yourself, you need
Map<String, List<Integer>> map = new HashMap<>();
Anyhow, the preffered way is to use a Guava Multimap
Put an ArrayList instance in the value part.
void addValue(Map map, Object key, Object value) {
Object obj = map.get(key);
List list;
if (obj == null) {
list = new ArrayList<Object>();
} else {
list = ((ArrayList) obj);
}
list.add(value);
map.put(key, list);
}
For More Info check this.
Use Map with value type as list of values..For example, in your map, while adding an entry, you will put key as "a" and you will have to add it's value as a list of Integer , having all the required values, like 1,2,3,4.
For a Map with entries with same key, has no sense to use get() .But as long as you use iterator() or entrySet() this should work:
class HashMap<String, String> {
Set<Entry<String, String>> entries;
#Override
public Set<Entry<String, String>> entrySet() {
return entries;
}
#Override
public int size() {
return entries.size();
}
public String put(String key, String value) {
if (entries == null) {
entries = new AbstractSet<Entry<String, String>>() {
ArrayList<Entry<String, String>> list = new ArrayList<>();
#Override
public Iterator<Entry<String, String>> iterator() {
return list.iterator();
}
#Override
public int size() {
return list.size();
}
#Override
public boolean add(Entry<String, String> stringStringEntry) {
return list.add(stringStringEntry);
}
};
}
StatusHandler.MyEntry entry = new StatusHandler.MyEntry();
entry.setKey(key);
entry.setValue(value);
entries.add(entry);
return value;
}
};
TL;DR So, what is it useful for? That comes from a hack to redmine-java-api to accept complex queries based on form params:
https://stackoverflow.com/a/18358659/848072
https://github.com/albfan/RedmineJavaCLI/commit/2bc51901f2f8252525a2d2258593082979ba7122

Java convert {String,String}[] to Map<String,String[]>

Given the class:
public class CategoryValuePair
{
String category;
String value;
}
And a method:
public Map<String,List<String>> convert(CategoryValuePair[] values);
Given that in values we can receive many entries with the same category, I want to convert these into a Map grouped on category.
Is there a quick / efficient way to perform this conversion?
As far as I know there is not easier way than iterating on values, and then putting the values in the map (like some predefined method).
Map<String, List<String>> map = new HashMap<String, List<String>>();
if (values != null) {
for (CategoryValuePair cvp : values) {
List<String> vals = map.get(cvp.category);
if (vals == null) {
vals = new ArrayList<String>();
map.put(cvp.category, vals);
}
vals.add(cvp.value);
}
}
I changed the map values from String[] to List<String> since it seems easier to me to use that so you don't have to hassle with array resizing.
To make it in fewer lines of code, use Google Collections:
public Map<String, Collection<String>> convert(CategoryValuePair[] values) {
Multimap<String, String> mmap = ArrayListMultimap.create();
for (CategoryValuePair value : values) {
mmap.put(value.category, value.value);
}
return mmap.asMap();
}
If you don't want to allow duplicate values, replace ArrayListMultimap with HashMultimap.
With lambdaj you just need one line of code to achieve that result as it follows:
group(values, by(on(CategoryValuePair.class).getCategory()));
Just for the sake of implementation... The method returns Map and also checks for duplicates in the arrays... though performance wise its heavy ...
public Map<String,String[]> convert(CategoryValuePair[] values)
{
Map<String, String[]> map = new HashMap<String, String[]>();
for (int i = 0; i < values.length; i++) {
if(map.containsKey(values[i].category)){
Set<String> set = new HashSet<String>(Arrays.asList(map.get(values[i].category)));
set.add(values[i].value);
map.put(values[i].category, set.toArray(new String[set.size()]));
}else {
map.put(values[i].category, new String[]{values[i].value});
}
}
return map;
}

Categories

Resources