Related
I'm grouping a list of objects in a Map<AvaliadorEventoDTO, Map<TipoQuestionario, List<EventoQuestaoDTO>>>, then I'm traversing map to create a new object with the information already grouped.
List<EventoQuestaoDTO> list = new ArrayList<EventoQuestaoDTO>(
eventoQuestaoService.buscarEventoQuestaoDTO(empresa, evento, idioma,
inscTipoTrabAval.getAvaliadorEvento().getId(), inscTipoTrabAval.getInscricaoTipoTrabalho().getId(), inscTipoTrabAval.getId()));
avaliacoes = new ArrayList<EventoAvaliacaoDTO>();
Map<AvaliadorEventoDTO, Map<TipoQuestionario, List<EventoQuestaoDTO>>> map = list.stream()
.collect(Collectors.groupingBy(EventoQuestaoDTO::getAvaliadorEvento, Collectors.groupingBy(EventoQuestaoDTO::getTipoQuestionario)));
for (Map.Entry<AvaliadorEventoDTO, Map<TipoQuestionario, List<EventoQuestaoDTO>>> avaliador : map.entrySet()) {
List<QuestionarioDTO> questionarios = new ArrayList<QuestionarioDTO>();
for(Map.Entry<TipoQuestionario, List<EventoQuestaoDTO>> questionario : avaliador.getValue().entrySet()) {
questionarios.add(new QuestionarioDTO(questionario.getKey(), questionario.getValue()));
}
avaliacoes.add(new EventoAvaliacaoDTO(avaliador.getKey(), questionarios));
}
But I would like to optimize the for in lambdas expressions, to transform the Map into a DTO QuestionarioDTO list, without needing to use the for and thus use fewer lines.
Could someone give a light?
The following should work:
List<EventoAvaliacaoDTO> avaliacoes = map.entrySet().stream().map(entry -> {
List<QuestionarioDTO> questionarios = entry.getValue().entrySet().stream().map(innerEntry ->
new QuestionarioDTO(innerEntry.getKey(), innerEntry.getValue())
).collect(Collectors.toList());
return new EventoAvaliacaoDTO(avaliador.getKey(), questionarios);
}).collect(Collectors.toList());
Is it more readable than nested for loops? I really doubt it. Keep in mind that not everything should be done using Java streams.
Here is a simple example:
Map<String, String> map = new HashMap<String, String>();
map.put("key 1", "value 1");
map.put("key 2", "value 2");
map.put("key 3", "value 3");
List<Object> list = new ArrayList<Object>();
map.forEach((key, value) -> list.add(key + " : " + value));
I want to be able to inverse a given HashMap that has multiple keys can point to the same value.
HashMap<String, String> cities = new HashMap<String, String>();
cities.put("Manchester", "UK");
cities.put("London", "UK");
static HashMap<String, String> inverseMap(HashMap map) {
// something that has "UK" point to both "Manchester" and "London"
// if it can be done without using any special Java 8 feature, that would be great
}
I am unsure where to start.
Do it like this. Basically, it employs a merge function which concatenates values for a duplicate key.
Create a new map
Use the values of the old map for the keys to the new
If the new map does not have a value for the key, put the value in the new map
Otherwise, concatenate the value to the old value for that key
HashMap<String, String> cities = new HashMap<String, String>();
cities.put("Manchester", "UK");
cities.put("London", "UK");
cities.put("New York", "US");
cities.put("Chicago", "US");
Map<String,String> inverted = new HashMap<>();
for (String key : cities.keySet()) {
String newKey = cities.get(key);
String value = inverted.get(newKey);
if (value == null) {
inverted.put(newKey, key);
} else {
value = value + ", " + key;
inverted.put(newKey, value);
}
}
for (Entry<String,String> e : inverted.entrySet()) {
System.out.println(e.getKey() + " -> " + e.getValue());
}
It prints
UK -> Manchester, London
US -> New York, Chicago
Since you didn't specify how to handle duplicate keys. I could also have stored it in a Map<String,List<String>>
Is this what you're looking for?
Map<String, String> cities = new HashMap<>();
cities.put("Manchester", "UK");
cities.put("London", "UK");
Map<String, List<String>> reverseMap = new HashMap<>();
for (Entry<String, String> entry : cities.entrySet()) {
List<String> list = reverseMap.get(entry.getValue());
if (list == null) {
list = new ArrayList<>();
reverseMap.put(entry.getValue(), list);
}
list.add(entry.getKey());
}
System.out.println(reverseMap);
As multiple keys can contain the same value, you will have to be able to store multiple values per key in the inversed Map. I recommend using a Set as a value for this.
Create a map that can save a Set of Strings as values
Iterate through the original map
If the country is not found in the new map, create an entry there
Add the city to the map
This should also work with Java 7 without dependencies.
static HashMap<String, Set<String>> inverseMap(HashMap<String,String> map) {
HashMap<String,Set<String>> inversed=new HashMap<>();
for(Map.Entry<String,String> entry:map.entrySet()){
if(!inversed.containsKey(entry.getValue())){
inversed.put(entry.getValue(),new HashSet<>());
}
inversed.get(entry.getValue()).add(entry.getKey());
}
return inversed;
}
{Manchester=UK,London=UK} would turn into {UK={Manchester,London}} (order may differ).
You can look at MultiMap. It allows mapping of a single key to multiple values.
This is something has been implemented in both Google Guava
https://guava.dev/releases/19.0/api/docs/com/google/common/collect/Multimap.html
ListMultimap<String, String> multimap = ArrayListMultimap.create();
for (President pres : US_PRESIDENTS_IN_ORDER) {
multimap.put(pres.firstName(), pres.lastName());
}
for (String firstName : multimap.keySet()) {
List<String> lastNames = multimap.get(firstName);
out.println(firstName + ": " + lastNames);
}
... produces output such as:
Zachary: [Taylor]
John: [Adams, Adams, Tyler, Kennedy] // Remember, Quincy!
George: [Washington, Bush, Bush]
Grover: [Cleveland, Cleveland] // Two, non-consecutive terms, rep'ing NJ!
As in Apache Commons Collections
https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/MultiValuedMap.html
MultiValuedMap<K, String> map = new MultiValuedHashMap<K, String>();
map.put(key, "A");
map.put(key, "B");
map.put(key, "C");
Collection<String> coll = map.get(key);
coll will be a collection containing "A", "B", "C".
You can iterate over the entry set of your original map and use the values (country code) as key and add each key (cities) to a list:
static HashMap<String, List<String>> inverseMap(HashMap<String, String> map) {
HashMap<String, List<String>> countryToCity = new HashMap<>();
for(Map.Entry<String,String> entry: map.entrySet()){
countryToCity.computeIfAbsent(entry.getValue(), k -> new ArrayList<>()).add(entry.getKey());
}
return countryToCity;
}
I have written this:
HashMap<String, String> map1 = new HashMap<String, String>();
Map<String, ArrayList<String>> map2 = new HashMap<String, ArrayList<String>>();
i am trying to allow more then 1 value for each key in a hashmap. so if the first key is '1', i want to allow '1' to be paired with values '2' and '3'.
so it be like:
1 --> 2
|--> 3
but when I do:
map2.put(key, value);
it gives error that says "incompatible types" and it can not be converted to ArrayList and it says the error is at the value part of the line.
If you are using Java 8, you can do this quite easily:
String key = "someKey";
String value1 = "someValue1";
String value2 = "someValue2";
Map<String, List<String>> map2 = new HashMap<>();
map2.computeIfAbsent(key, k -> new ArrayList<>()).add(value1);
map2.computeIfAbsent(key, k -> new ArrayList<>()).add(value2);
System.out.println(map2);
The documentation for Map.computeIfAbsent(...) has pretty much this example.
In map2 you need to add ArrayList (you declared it as Map<String, ArrayList<String>> - the second one is the value type) only, that's why it gives you incompatible types.
You would need to do initialize the key with an ArrayList and add objects to it later:
if (!map2.containsKey(key)) {
map2.put(key, new ArrayList<String>());
}
map2.get(key).add(value);
Or you could use Multimap from guava, then you can just map2.put and it won't overwrite your values there but add to a list.
You are little bit away from what you are trying to do.
Map<String, ArrayList<String>> map2 = new HashMap<String, ArrayList<String>>();
this will allow only String as key and an ArrayList as value. So you have to try something like:
ArrayList<String> value=new ArrayList<String>();
value.add("2");
value.add("3");
map2.put("1", value);
When retrieving you also have to follow ans opposite procedure.
ArrayList<String> valueTemp=map2.get("1");
then you can iterate over this ArrayList to get those values ("2" and "3");
Try like this. //use list or set.. but set avoids duplicates
Map<String, Set<String>> map = new HashMap<>();
Set<String> list = new HashSet<>();
// add value to the map
Boolean b = map.containsKey(key);
if (b) {
map.get(key).addAll(list);
} else
map.put(key, list);
}
You can not add different values in same key in Map. Map is override the value in that key. You can do like this way.
Map<String, ArrayList<String>> map = new HashMap<String, ArrayList<String>>();
ArrayList<String> list=new ArrayList<String>();
list.add("2");
list.add("3");
map.put("1", list);
first add value in array list then put into map.
It is all because standard Map implementations in java stores only single pairs (oneKey, oneValue). The only way to store multiple values for a particular key in a java standard Map is to store "collection" as value, then you need to access this collection (from Map) by key, and then use this collection "value" as regular collection, in your example as ArrayList. So you do not put something directly by map.put (except from creating the empty collection), instead you take the whole collection by key and use this collection.
You need something like Multimap, for example:
public class Multimap<T,S> {
Map<T, ArrayList<S>> map2 = new HashMap<T, ArrayList<S>>();
public void add(T key, S value) {
ArrayList<T> currentValuesForGivenKey = get(key);
if (currentValuesForGivenKey == null) {
currentValuesForGivenKey = new ArrayList<T>();
map2.get(key, currentValuesForGivenKey);
}
currentValuesForGivenKey.add(value);
}
public ArrayList<S> get(T key) {
ArrayList<String> currentValuesForGivenKey = map2.get(key);
if (currentValuesForGivenKey == null) {
currentValuesForGivenKey = new ArrayList<S>();
map2.get(key, currentValuesForGivenKey);
}
return currentValuesForGivenKey;
}
}
then you can use it like this:
Multimap<String,String> map2 = new Multimap<String,String>();
map2.add("1","2");
map2.add("1","3");
map2.add("1","4");
for (String value: map2.get("1")) {
System.out.println(value);
}
will print:
2
3
4
it gives error that says "incompatible types" and it can not be converted to ArrayList and it says the error is at the value part of the line.
because, it won't automatically convert to ArrayList.
You should add both the values to list and then put that list in map.
I'm just starting to learn to use HashMap and reading the java tutorial, but I'm having trouble.
I'm trying to update the List inside a HashMap but I want to get the List of that key, is there a way to update a specific List of the key instead of having to make...5 different Lists and updating those?
HashMap<String, ArrayList<String>> mMap = new HashMap<String, ArrayList<String>>();
ArrayList<String> list = new ArrayList<String>();
mMap.put("A", list);
mMap.put("B", list);
mMap.put("C", list);
mMap.put("D", list);
Iterator iter = mMap.entrySet().iterator();
if (mMap.containsKey("A"))
{
Map.Entry mEntry = (Map.Entry) iter.next();
list.add("test");
mMap.put("A",list);
System.out.println(mEntry.getKey() + " : " + mEntry.getValue());
}
else if (mMap.containsKey("B"))
{
Map.Entry mEntry = (Map.Entry) iter.next();
list.add("entry");
mMap.put("B",list);
System.out.println(mEntry.getKey() + " : " + mEntry.getValue());
}
You could use something like:
mMap.get("A").add("test");
mMap.get("B").add("entry");
Like #Tudor said, use mMap.get("A").add("foo");
You put the same exact list into each map entry. You initial lines should be
mMap.put("A", new ArrayList());
mMap.put("B", new ArrayList());
...
mMap.put("Z", new ArrayList());
Alternatvely, write a method that checks on the fly
public synchronized void myAdd(String key, String value) {
List<String> there = mMap.get(key);
if (there == null) {
there = new ArrayList<String>();
mMap.put(key, there);
}
there.add(value);
}
you probably mean:
HashMap<String, List<String>> mMap = new HashMap<String, List<String>>();
mMap.put("A", new ArrayList<String>());
mMap.put("B", new ArrayList<String>());
mMap.put("C", new ArrayList<String>());
mMap.put("D", new ArrayList<String>());
if (mMap.containsKey("A"))
{
mMap.get("A").add("test");
System.out.println(mEntry.getKey() + " : " + mEntry.getValue());
}
else if (mMap.containsKey("B"))
{
mMap.get("B").add("entry");
System.out.println(mEntry.getKey() + " : " + mEntry.getValue());
}
...
I wonder if you really need those containsKey checks either!
HTH!
I think that in your case you can use Google Guava's Multimap and ListMultimap interfaces and ArrayListMultimap implementation.
Choosing right collection (in link there are only standard collections but Multimap is right in this case) makes code more readable:
ListMultimap<String, String> myMultimap = ArrayListMultimap.create();
myMultimap.put("A", "test");
myMultimap.put("B", "entry");
then myMultimap.get("A") gives you list (ArrayList instance in fact) with one element: "test", while myMultimap.get("C") gives you empty list.
Comparing to Map<String, List<String>> approach:
you don't have to initialize "C" key with empty list for it,
you don't have nulls checks (no NullPointerExceptions),
you don't have to do other checks such as myMap.containsKey("A"),
you write less code,
so your code is less bug-prone,
etc., etc.
P.S. Instead of:
HashMap<String, ArrayList<String>> mMap = new HashMap<String, ArrayList<String>>();
use interfaces, not classes when using collections i.e.:
Map<String, List<String>> mMap = new HashMap<String, List<String>>();
and even better Guava's "static constructors" (you don't repeat code):
Map<String, List<String>> mMap = Maps.newHashMap()
when knowledge about implementation is not necessary.
If you add the same list as a value with different keys, in your case keys A,B,C, and D all point to the same list, and access and update the list through one key the changes will then be visible in all the lists. Each key points to the same list structure.
If you want the lists to be different you need to use different for different keys you need to use a different list.
You could automate the process, say by making your own insert method that clones the given list.
I would do something like this.
private final ReentrantLock lock = new ReentrantLock();
Map<String ,List<String>> mMap=new HashMap<>();
public void myAdd(String key, String value) {
try {
lock.lock();
List<String> there =mMap.get(key)==null?new ArrayList<>():mMap.get(key);
there.add(value);
mMap.put(key, there);
}finally {
lock.unlock();
}
}
How can I create and fetch associative arrays in Java like I can in PHP?
For example:
$arr[0]['name'] = 'demo';
$arr[0]['fname'] = 'fdemo';
$arr[1]['name'] = 'test';
$arr[1]['fname'] = 'fname';
Java doesn't support associative arrays, however this could easily be achieved using a Map. E.g.,
Map<String, String> map = new HashMap<String, String>();
map.put("name", "demo");
map.put("fname", "fdemo");
// etc
map.get("name"); // returns "demo"
Even more accurate to your example (since you can replace String with any object that meet your needs) would be to declare:
List<Map<String, String>> data = new ArrayList<>();
data.add(0, map);
data.get(0).get("name");
See the official documentation for more information
Java doesn't have associative arrays like PHP does.
There are various solutions for what you are doing, such as using a Map, but it depends on how you want to look up the information. You can easily write a class that holds all your information and store instances of them in an ArrayList.
public class Foo{
public String name, fname;
public Foo(String name, String fname){
this.name = name;
this.fname = fname;
}
}
And then...
List<Foo> foos = new ArrayList<Foo>();
foos.add(new Foo("demo","fdemo"));
foos.add(new Foo("test","fname"));
So you can access them like...
foos.get(0).name;
=> "demo"
You can accomplish this via Maps. Something like
Map<String, String>[] arr = new HashMap<String, String>[2]();
arr[0].put("name", "demo");
But as you start using Java I am sure you will find that if you create a class/model that represents your data will be your best options. I would do
class Person{
String name;
String fname;
}
List<Person> people = new ArrayList<Person>();
Person p = new Person();
p.name = "demo";
p.fname = "fdemo";
people.add(p);
Look at the Map interface, and at the concrete class HashMap.
To create a Map:
Map<String, String> assoc = new HashMap<String, String>();
To add a key-value pair:
assoc.put("name", "demo");
To retrieve the value associated with a key:
assoc.get("name")
And sure, you may create an array of Maps, as it seems to be what you want:
Map<String, String>[] assoc = ...
There is no such thing as associative array in Java. Its closest relative is a Map, which is strongly typed, however has less elegant syntax/API.
This is the closest you can get based on your example:
Map<Integer, Map<String, String>> arr =
org.apache.commons.collections.map.LazyMap.decorate(
new HashMap(), new InstantiateFactory(HashMap.class));
//$arr[0]['name'] = 'demo';
arr.get(0).put("name", "demo");
System.out.println(arr.get(0).get("name"));
System.out.println(arr.get(1).get("name")); //yields null
Well i also was in search of Associative array and found the List of maps as the best solution.
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class testHashes {
public static void main(String args[]){
Map<String,String> myMap1 = new HashMap<String, String>();
List<Map<String , String>> myMap = new ArrayList<Map<String,String>>();
myMap1.put("URL", "Val0");
myMap1.put("CRC", "Vla1");
myMap1.put("SIZE", "Vla2");
myMap1.put("PROGRESS", "Vla2");
myMap.add(0,myMap1);
myMap.add(1,myMap1);
for (Map<String, String> map : myMap) {
System.out.println(map.get("URL"));
}
//System.out.println(myMap);
}
}
Java equivalent of Perl's hash
HashMap<Integer, HashMap<String, String>> hash;
Java doesn't have associative arrays, the closest thing you can get is the Map interface
Here's a sample from that page.
import java.util.*;
public class Freq {
public static void main(String[] args) {
Map<String, Integer> m = new HashMap<String, Integer>();
// Initialize frequency table from command line
for (String a : args) {
Integer freq = m.get(a);
m.put(a, (freq == null) ? 1 : freq + 1);
}
System.out.println(m.size() + " distinct words:");
System.out.println(m);
}
}
If run with:
java Freq if it is to be it is up to me to delegate
You'll get:
8 distinct words:
{to=3, delegate=1, be=1, it=2, up=1, if=1, me=1, is=2}
Use ArrayList < Map < String, String > >
Here a code sample :
ArrayList<Map<String, String>> products = new ArrayList<Map<String, String>>();
while (iterator.hasNext()) {
Map<String, String> product = new HashMap<String, String>();
Element currentProduct = iterator.next();
product.put("id",currentProduct.get("id"));
product.put("name" , currentProduct.get("name") );
products.add(product );
}
System.out.println("products : " + products);
Output :
products : [{id=0001, name=prod1}, {id=0002, name=prod2}]
Associative arrays in Java like in PHP :
SlotMap hmap = new SlotHashMap();
String key = "k01";
String value = "123456";
// Add key value
hmap.put( key, value );
// check if key exists key value
if ( hmap.containsKey(key)) {
//.....
}
// loop over hmap
Set mapkeys = hmap.keySet();
for ( Iterator iterator = mapkeys.iterator(); iterator.hasNext();) {
String key = (String) iterator.next();
String value = hmap.get(key);
}
More info, see Class SoftHashMap : https://shiro.apache.org/static/1.2.2/apidocs/org/apache/shiro/util/SoftHashMap.html
Object[][] data = {
{"mykey1", "myval1"},
{"mykey2", "myval2"},
{new Date(), new Integer(1)},
};
Yes, this require iteration for searchting value by key, but if you need all of them, this will be the best choice.
In JDK 1.5 (http://tinyurl.com/3m2lxju) there is even a note: "NOTE: This class is obsolete. New implementations should implement the Map interface, rather than extending this class."
Regards, N.
Actually Java does support associative arrays they are called dictionaries!
Thinking more about it, I would like to throw out tuples as a more general-purpose way of dealing with this problem. While tuples are not native to Java, I use Javatuples to provide me the same functionality which would exist in other languages. An example of how to deal with the question asked is
Map<Pair<Integer, String>, String> arr = new HashMap<Pair<Integer, String>, String>();
Pair p1 = new Pair(0, "name");
arr.put(p1, "demo");
I like this approach because it can be extended to triples and other higher ordered groupings with api provided classes and methods.
Regarding the PHP comment 'No, PHP wouldn't like it'. Actually, PHP would keep on chugging unless you set some very restrictive (for PHP) exception/error levels, (and maybe not even then).
What WILL happen by default is that an access to a non existing variable/out of bounds array element 'unsets' your value that you're assigning to. NO, that is NOT null. PHP has a Perl/C lineage, from what I understand. So there are: unset and non existing variables, values which ARE set but are NULL, Boolean False values, then everything else that standard langauges have. You have to test for those separately, OR choose the RIGHT evaluation built in function/syntax.