I am trying to build a dynamic hashmap of type String and arraylist dynamically. I have some json data coming from server and instead of declaring many arraylist I want to save them in hashmap with String as key and arraylist as value.
Here is what I am doing now
ArrayList<classproperty> allStu;
ArrayList<classproperty> allEmp;
HashMap<String, ArrayList<classproperty>> hash;
if (type.equals("Student")) {
prop = new classproperty("Student", info.getJSONObject(i).getJSONObject("student").getJSONArray("class").getJSONObject(s).getJSONObject("type").getString("name"));
allStu.add(prop);
}
if (type.equals("Emp")) {
prop = new esSignalProperty("Emp", info.getJSONObject(m).getJSONObject("emp").getJSONObject(s).getJSONObject("dept").getString("name"));
allemp.add(prop);
}
hash.put("Student", allStu);
hash.put("Emp", allemp);
So it is ugly way to do it...and I would like to do it by directly putting into hashmap without declaring so many arraylist. please ignore json string extraction as it is just dummy.
You just need to initialize the arraylist in the beginning and then just add value based on key. if you know the keys that I guess you know you can do like this
public HashMap<String, ArrayList<classproperty>> hash
hash.put("Student", new ArrayList<classproperty>());
hash.put("Emp", new ArrayList<classproperty>());
after just as #steffen mention but with minor change
hash.get("Student").add(prop);
hash.get("Emp").add(prop);
It is nothing very different from what other purposed but may be still can help.
hash.get("Student").put(prop)
could be a solution, as you know the key inside the map.
Using this way you can leave out the 'allStu' and 'allEmp' Lists, as you can get them directly from the map.
I would suggest using MultiMap from Guava library that already supports this. If you don't plan to import this library, then you can roll your own manually as a wrapper of a Map<K, List<V>>:
//basic skeleton of the multimap
//as a wrapper of a map
//you can define more methods as you want/need
public class MyMultiMap<K,V> {
Map<K, List<V>> map;
public MyMultiMap() {
map = new HashMap<K, List<V>>();
}
//in case client needs to use another kind of Map for implementation
//e.g. ConcurrentHashMap
public MyMultiMap(Map<K, List<V>> map) {
this.map = map;
}
public void put(K key, V value) {
List<V> values = map.get(key);
if (values == null) {
//ensure that there will always be a List
//for any key/value to be inserted
values = new ArrayList<V>();
map.put(key, values);
}
values.add(value);
}
public List<V> get(K key) {
return map.get(key);
}
#Override
public String toString() {
//naive toString implementation
return map.toString();
}
}
Then just use your multimap:
MyMultiMap myMultiMap = new MyMultiMap<String, ClassProperty>();
myMultiMap.put("student", new ClassProperty(...));
myMultiMap.put("student", new ClassProperty(...));
System.out.println(myMultiMap);
Related
I want your help for creating a data structure/ Collection which supports read and insert functionality and prevents deletion/removal of records.
One way i could think of is , to create a customized collection ( like myHashMap ) and override all the delete/remove methods and thus prevent removal/deletion of records? But this approach will not work if the Object is having removal method as Final.
Please suggest any better way ..!!!!
You can create your own arraylist by overriding the methods like remove() with unsupported operation exception. If you want you can also declare arraylist like this.
ArrayList<String> myList = new ArrayList<String>() {
#Override
public boolean remove(String str) {
//If you want, you can throw Unsupported operation exception also.
return false;
}
};
There may be some other good approaches, you can check answers from others also.
You can create your Map wrapper and provide only the methods you want to :
class MyHashMap<K, V> {
Map<K, V> map;
public MyHashMap(Map<K, V> map) {
this.map = map;
}
public V put(K key, V value) {
return map.put(key, value);
}
public V get(K key) {
return map.get(key);
}
// other methods you want
}
example:
MyHashMap<String, String> myMap = new MyHashMap<>(new HashMap<>());
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.
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
I am trying to use a HashMap to map a unique string to a string ArrayList like this:
HashMap<String, ArrayList<String>>
Basically, I want to be able to access the keys by number, not by using the key's name. And I want to be able to access said key's value, to iterate over it. I'm imagining something like this:
for(all keys in my hashmap) {
for(int i=0; i < myhashmap.currentKey.getValue.size(); i++) {
// do things with the hashmaps elements
}
}
Is there an easy way to do this?
Here is the general solution if you really only want the first key's value
Object firstKey = myHashMap.keySet().toArray()[0];
Object valueForFirstKey = myHashMap.get(firstKey);
You can iterate over keys by calling map.keySet(), or iterate over the entries by calling map.entrySet(). Iterating over entries will probably be faster.
for (Map.Entry<String, List<String>> entry : map.entrySet()) {
List<String> list = entry.getValue();
// Do things with the list
}
If you want to ensure that you iterate over the keys in the same order you inserted them then use a LinkedHashMap.
By the way, I'd recommend changing the declared type of the map to <String, List<String>>. Always best to declare types in terms of the interface rather than the implementation.
HashMaps are not ordered, unless you use a LinkedHashMap or SortedMap. In this case, you may want a LinkedHashMap. This will iterate in order of insertion (or in order of last access if you prefer). In this case, it would be
int index = 0;
for ( Map.Entry<String,ArrayList<String>> e : myHashMap.iterator().entrySet() ) {
String key = e.getKey();
ArrayList<String> val = e.getValue();
index++;
}
There is no direct get(index) in a map because it is an unordered list of key/value pairs. LinkedHashMap is a special case that keeps the order.
Kotlin HashMap Answer
You can get key by index. Then get value by key.
val item = HashMap<String, String>() // Dummy HashMap.
val keyByIndex = item.keys.elementAt(0) // Get key by index. I selected "0".
val valueOfElement = item.getValue(keyByIndex) // Get value.
You can do:
for(String key: hashMap.keySet()){
for(String value: hashMap.get(key)) {
// use the value here
}
}
This will iterate over every key, and then every value of the list associated with each key.
A solution is already selected. However, I post this solution for those who want to use an alternative approach:
// use LinkedHashMap if you want to read values from the hashmap in the same order as you put them into it
private ArrayList<String> getMapValueAt(LinkedHashMap<String, ArrayList<String>> hashMap, int index)
{
Map.Entry<String, ArrayList<String>> entry = (Map.Entry<String, ArrayList<String>>) hashMap.entrySet().toArray()[index];
return entry.getValue();
}
for (Object key : data.keySet()) {
String lKey = (String) key;
List<String> list = data.get(key);
}
I came across the same problem, read a couple of answers from different related questions and came up with my own class.
public class IndexableMap<K, V> extends HashMap<K, V> {
private LinkedList<K> keyList = new LinkedList<>();
#Override
public V put(K key, V value) {
if (!keyList.contains(key))
keyList.add(key);
return super.put(key, value);
}
#Override
public void putAll(Map<? extends K, ? extends V> m) {
for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
#Override
public void clear() {
keyList.clear();
super.clear();
}
public List<K> getKeys() {
return keyList;
}
public int getKeyIndex(K key) {
return keyList.indexOf(key);
}
public K getKeyAt(int index) {
if (keyList.size() > index)
return keyList.get(index);
return null;
}
public V getValueAt(int index) {
K key = getKeyAt(index);
if (key != null)
return get(key);
return null;
}
}
Example (types are differing from OPs question just for clarity):
Map<String, Double> myMap = new IndexableMap<>();
List<String> keys = myMap.getKeys();
int keyIndex = myMap.getKeyIndex("keyString");
String key = myMap.getKeyAt(2);
Double value myMap.getValueAt(2);
Keep in mind that it does not override any of the complex methods, so you will need to do this on your own if you want to reliably access one of these.
Edit: I made a change to the putAll() method, because the old one had a rare chance to cause HashMap and LinkedList being in different states.
Try this:
myhashmap.entrySet()
.forEach{
println(it.getKey())
println(it.getValue())
}
or if you want by index
myhashmap.entrySet()[0].getKey()
myhashmap.entrySet()[0].getValue()
myhashmap.entrySet()[1].getKey()
myhashmap.entrySet()[1].getValue()
HashMaps don't keep your key/value pairs in a specific order. They are ordered based on the hash that each key's returns from its Object.hashCode() method. You can however iterate over the set of key/value pairs using an iterator with:
for (String key : hashmap.keySet())
{
for (list : hashmap.get(key))
{
//list.toString()
}
}
If you don't care about the actual key, a concise way to iterate over all the Map's values would be to use its values() method
Map<String, List<String>> myMap;
for ( List<String> stringList : myMap.values() ) {
for ( String myString : stringList ) {
// process the string here
}
}
The values() method is part of the Map interface and returns a Collection view of the values in the map.
You can use Kotlin extension function
fun LinkedHashMap<String, String>.getKeyByPosition(position: Int) =
this.keys.toTypedArray()[position]
fun LinkedHashMap<String, String>.getValueByPosition(position: Int) =
this.values.toTypedArray()[position]
You'll need to create multiple HashMaps like this for example
Map<String, String> fruitDetails = new HashMap();
fruitDetails.put("Mango", "Mango is a delicious fruit!");
fruitDetails.put("Guava" "Guava is a delicious fruit!");
fruitDetails.put("Pineapple", "Pineapple is a delicious fruit!");
Map<String, String> fruitDetails2 = new HashMap();
fruitDetails2.put("Orange", "Orange is a delicious fruit!");
fruitDetails2.put("Banana" "Banana is a delicious fruit!");
fruitDetails2.put("Apple", "Apple is a delicious fruit!");
// STEP 2: Create a numeric key based HashMap containing fruitDetails so we can access them by index
Map<Integer, Map<String, String>> hashMap = new HashMap();
hashMap.put(0, fruitDetails);
hashMap.put(1, fruitDetails2);
// Now we can successfully access the fruitDetails by index like this
String fruit1 = hashMap.get(0).get("Guava");
String fruit2 = hashMap.get(1).get("Apple");
System.out.println(fruit1); // outputs: Guava is a delicious fruit!
System.out.println(fruit2); // outputs: Apple is a delicious fruit!
I'm looking for a class in java that has key-value association, but without using hashes. Here is what I'm currently doing:
Add values to a Hashtable.
Get an iterator for the Hashtable.entrySet().
Iterate through all values and:
Get a Map.Entry for the iterator.
Create an object of type Module (a custom class) based on the value.
Add the class to a JPanel.
Show the panel.
The problem with this is that I do not have control over the order that I get the values back, so I cannot display the values in the a given order (without hard-coding the order).
I would use an ArrayList or Vector for this, but later in the code I need to grab the Module object for a given Key, which I can't do with an ArrayList or Vector.
Does anyone know of a free/open-source Java class that will do this, or a way to get values out of a Hashtable based on when they were added?
Thanks!
I suggest a LinkedHashMap or a TreeMap. A LinkedHashMap keeps the keys in the order they were inserted, while a TreeMap is kept sorted via a Comparator or the natural Comparable ordering of the keys.
Since it doesn't have to keep the elements sorted, LinkedHashMap should be faster for most cases; TreeMap has O(log n) performance for containsKey, get, put, and remove, according to the Javadocs, while LinkedHashMap is O(1) for each.
If your API that only expects a predictable sort order, as opposed to a specific sort order, consider using the interfaces these two classes implement, NavigableMap or SortedMap. This will allow you not to leak specific implementations into your API and switch to either of those specific classes or a completely different implementation at will afterwards.
LinkedHashMap will return the elements in the order they were inserted into the map when you iterate over the keySet(), entrySet() or values() of the map.
Map<String, String> map = new LinkedHashMap<String, String>();
map.put("id", "1");
map.put("name", "rohan");
map.put("age", "26");
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + " = " + entry.getValue());
}
This will print the elements in the order they were put into the map:
id = 1
name = rohan
age = 26
If an immutable map fits your needs then there is a library by google called guava (see also guava questions)
Guava provides an ImmutableMap with reliable user-specified iteration order. This ImmutableMap has O(1) performance for containsKey, get. Obviously put and remove are not supported.
ImmutableMap objects are constructed by using either the elegant static convenience methods of() and copyOf() or a Builder object.
You can use LinkedHashMap to main insertion order in Map
The important points about Java LinkedHashMap class are:
It contains only unique elements.
A LinkedHashMap contains values based on the key.
It may have one null key and multiple null values.
It is same as HashMap instead maintains insertion order
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>
But if you want sort values in map using User-defined object or any primitive data type key then you should use TreeMap For more information, refer this link
You can maintain a Map (for fast lookup) and List (for order) but a LinkedHashMap may be the simplest. You can also try a SortedMap e.g. TreeMap, which an have any order you specify.
Either You can use LinkedHashMap<K, V> or you can implement you own CustomMap which maintains insertion order.
You can use the Following CustomHashMap with the following features:
Insertion order is maintained, by using LinkedHashMap internally.
Keys with null or empty strings are not allowed.
Once key with value is created, we are not overriding its value.
HashMap vs LinkedHashMap vs CustomHashMap
interface CustomMap<K, V> extends Map<K, V> {
public boolean insertionRule(K key, V value);
}
#SuppressWarnings({ "rawtypes", "unchecked" })
public class CustomHashMap<K, V> implements CustomMap<K, V> {
private Map<K, V> entryMap;
// SET: Adds the specified element to this set if it is not already present.
private Set<K> entrySet;
public CustomHashMap() {
super();
entryMap = new LinkedHashMap<K, V>();
entrySet = new HashSet();
}
#Override
public boolean insertionRule(K key, V value) {
// KEY as null and EMPTY String is not allowed.
if (key == null || (key instanceof String && ((String) key).trim().equals("") ) ) {
return false;
}
// If key already available then, we are not overriding its value.
if (entrySet.contains(key)) { // Then override its value, but we are not allowing
return false;
} else { // Add the entry
entrySet.add(key);
entryMap.put(key, value);
return true;
}
}
public V put(K key, V value) {
V oldValue = entryMap.get(key);
insertionRule(key, value);
return oldValue;
}
public void putAll(Map<? extends K, ? extends V> t) {
for (Iterator i = t.keySet().iterator(); i.hasNext();) {
K key = (K) i.next();
insertionRule(key, t.get(key));
}
}
public void clear() {
entryMap.clear();
entrySet.clear();
}
public boolean containsKey(Object key) {
return entryMap.containsKey(key);
}
public boolean containsValue(Object value) {
return entryMap.containsValue(value);
}
public Set entrySet() {
return entryMap.entrySet();
}
public boolean equals(Object o) {
return entryMap.equals(o);
}
public V get(Object key) {
return entryMap.get(key);
}
public int hashCode() {
return entryMap.hashCode();
}
public boolean isEmpty() {
return entryMap.isEmpty();
}
public Set keySet() {
return entrySet;
}
public V remove(Object key) {
entrySet.remove(key);
return entryMap.remove(key);
}
public int size() {
return entryMap.size();
}
public Collection values() {
return entryMap.values();
}
}
Usage of CustomHashMap:
public static void main(String[] args) {
System.out.println("== LinkedHashMap ==");
Map<Object, String> map2 = new LinkedHashMap<Object, String>();
addData(map2);
System.out.println("== CustomHashMap ==");
Map<Object, String> map = new CustomHashMap<Object, String>();
addData(map);
}
public static void addData(Map<Object, String> map) {
map.put(null, "1");
map.put("name", "Yash");
map.put("1", "1 - Str");
map.put("1", "2 - Str"); // Overriding value
map.put("", "1"); // Empty String
map.put(" ", "1"); // Empty String
map.put(1, "Int");
map.put(null, "2"); // Null
for (Map.Entry<Object, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + " = " + entry.getValue());
}
}
O/P:
== LinkedHashMap == | == CustomHashMap ==
null = 2 | name = Yash
name = Yash | 1 = 1 - Str
1 = 2 - Str | 1 = Int
= 1 |
= 1 |
1 = Int |
If you know the KEY's are fixed then you can use EnumMap. Get the values form Properties/XML files
EX:
enum ORACLE {
IP, URL, USER_NAME, PASSWORD, DB_Name;
}
EnumMap<ORACLE, String> props = new EnumMap<ORACLE, String>(ORACLE.class);
props.put(ORACLE.IP, "127.0.0.1");
props.put(ORACLE.URL, "...");
props.put(ORACLE.USER_NAME, "Scott");
props.put(ORACLE.PASSWORD, "Tiget");
props.put(ORACLE.DB_Name, "MyDB");
I don't know if it is opensource, but after a little googling, I found this implementation of Map using ArrayList. It seems to be pre-1.5 Java, so you might want to genericize it, which should be easy. Note that this implementation has O(N) access, but this shouldn't be a problem if you don't add hundreds of widgets to your JPanel, which you shouldn't anyway.
Whenever i need to maintain the natural order of things that are known ahead of time, i use a EnumMap
the keys will be enums and you can insert in any order you want but when you iterate it will iterate in the enum order (the natural order).
Also when using EnumMap there should be no collisions which can be more efficient.
I really find that using enumMap makes for clean readable code.
Here is an example