I created a class Foo that has the method toArray() that returns an Array<Int>.
Now, I have a HashMap mapping Strings to HashMaps, which map Objects to Foo. That is:
HashMap<String,HashMap<Object,Foo>>
And I want to create a new object of type:
HashMap<String,HashMap<Object,Array<Int>>>
That is obtained by calling the function toArray() for every element Foo in the original HashMAp.
To do so I normally would do something like:
public static HashMap<String,HashMap<Object,Array<Int>>> changeMap(Map mpOld) {
Object key2;
String key1;
Iterator it2;
HashMap<String,HashMap<Object,Array<Int>>> mpNew=
new HashMap<String,HashMap<Object,Array<Int>>>()
Iterator it1 = mpOld.keySet().iterator();
while (it1.hasNext()) {
key1=it1.next();
it2= mpOld.get(key1).keySet().iterator();
mpNew.put(key1,new HashMap<Object,Array<Int>>())
while (it2.hasNext()) {
key2=it2.next();
mpNew.get(key1).put(key2,mpOld.get(key1).get(key2).toArray());
//TODO clear entry mpOld.get(key1).get(key2)
}
//TODO clear entry mpOld.get(key1)
}
return mpNew;
}
A similar code works just fine, but the Size of the HashMap is too big to hold two of them in memory. As you can see I added two points where I want to clear some entries. The problem is, if I do, I get either a concurrency error, or the iterator loop just terminates.
I wonder if there is a better way to iterate through the Maps and copy the information.
Also, I'm working in a Scala project but here I have to use Java types for some compatibility issues. Although Java.util.HashMap is not an iterator, maybe Scala has some hidden functinality to deal with this?
Thanks,
Iterators offer remove(..) methods that safely removes the previously accessed item. Iterate over the Key/Value entries of the map, converting them and adding them to the new map, and removing the old ones as you go.
/**
* Transfers and converts all entries from <code>map1</code> to
* <code>map2</code>. Specifically, the {#link Foo} objects of the
* inner maps will be converted to integer arrays via {#link Foo#toArray}.
*
* #param map1 Map to be emptied.
* #param map2 Receptacle for the converted entries.
*/
private static void transfer(Map<String, Map<Object, Foo>> map1
, Map<String, Map<Object, int[]>> map2) {
final Iterator<Entry<String, Map<Object, Foo>>> mapIt
= map1.entrySet().iterator();
while (mapIt.hasNext()) {
final Entry<String, Map<Object, Foo>> mapEntry = mapIt.next();
mapIt.remove();
final Map<Object, int[]> submap = new HashMap<Object,int[]>();
map2.put(mapEntry.getKey(), submap);
final Iterator<Entry<Object,Foo>> fooIt
= mapEntry.getValue().entrySet().iterator();
while (fooIt.hasNext()) {
final Entry<Object,Foo> fooEntry = fooIt.next();
fooIt.remove();
submap.put(fooEntry.getKey(), fooEntry.getValue().toArray());
}
}
}
I did not have time to check it, but I guess something like this should work on scala Maps (assuming you use scala 2.8 which is finally here):
mpO.mapValues(_.mapValues(_.toArray))
It would take your outer map, and "replace" all inner maps with a new one, where the values are the Int arrays. Keys, and the general "structure" of the maps remain the same. According to scaladoc "The resulting map wraps the original map without copying any elements.", so it won't be a real replacement.
If you also do an
import scala.collection.JavaConversions._
then the java maps can be used the same way as scala maps: JavaConversions contain a bunch of implicit methods that can convert between scala and java collections.
BTW using a Map < String,HashMap < Object,Array < Int>>> might not be really convenient at the end, if I were you I would consider introducing some classes that would hide the complexity of this construct.
Edit reflecting to your comment
import scala.collection.JavaConversions._
import java.util.Collections._
object MapValues {
def main(args: Array[String]) {
val jMap = singletonMap("a",singletonMap("b", 1))
println(jMap)
println(jMap.mapValues(_.mapValues(_+1)))
}
}
prints:
{a={b=1}}
Map(a -> Map(b -> 2))
Showing that the implicits are applied both to the outer and inner map quite nicely. This is the purpose of the JavaConversions object: even if you have a java collection you can use it as a similar scala class (with boosted features).
You don't have to do anything else, just import JavaConversions._
For example considering String keys; lets call the input data: Map<String, Map<String, Object>> data
for (Entry<String, Map<String, Tuple>> entry : data.entrySet()) {
String itemKey = entry.getKey();
for (Entry<String, Object> innerEntry : entry.getValue().entrySet()) {
String innerKey = innerEntry.getKey();
Object o = innerEntry.getValue();
// whatever, here you have itemKey, innerKey and o
}
}
The set is backed by the map, so changes to the map are reflected in the set, and vice-versa. If the map is modified while an iteration over the set is in progress (except through the iterator's own remove operation), the results of the iteration are undefined. The set supports element removal, which removes the corresponding mapping from the map, via the Iterator.remove, Set.remove, removeAll, retainAll, and clear operations.
Why don't you call the remove () method on the iterator or set.remove (iterator.next ()) where iterator.next () returns the key, set is the keyset and iterator its iterator.
PS: also try to refactor your data structure, maybe some intermediate classes which handle the data retrieval? A map in a map with arrays as values doesn't say anything and is difficult to keep track of.
Related
Sorry maybe for dumb question. I am looking for elegant way to go over elements of my map and filter properties.
Let's say I have map with two elements.
Map<String, MyElement> myMap;
This is how looks my element
class MyElement {
Map <String, Property1> properties1;
Map <String, Property2> properties2;
}
MyElement[0] includes properties1 map filled with some properties, and properties2 is null.
MyElement[1] includes properties2 map filled with some properties, and properties1 is null.
It might be vise versa, I have no idea for which MyElelmet Internal Maps are null and for which are not.
I would like to go over each MyElement in map and assemble properties1 or properties2 from each element in case if it is not empty.
Result should be two separate maps (new collections)
Map <String, Property1> assembledProperties1;
Map <String, Property2> assembledProperties2;
You can think about it as a collecting results to multiple outputs (assembledProperties1, assembledProperties2).
Is there any elegant way to do it with Java streams, without ugly if statements?
Since don't want to utilize MyElement as a mutable container, you can define a special type of object that will carry references to the maps of properties.
In order to be able to perform mutable reduction on a stream of type MyElement with this object we need to define a method that will expect MyElement as a parameter to update maps based on the next element of the stream, and another method that is needed to merge partial results of execution in parallel (i.e. to combine the two objects).
public class PropertyWrapper {
private Map<String, Property1> properties1 = new HashMap<>();
private Map<String, Property2> properties2 = new HashMap<>();
public PropertyWrapper merge(MyElement element) {
if (element.getProperties1() != null) properties1.putAll(element.getProperties1());
if (element.getProperties2() != null) properties2.putAll(element.getProperties2());
return this;
}
public PropertyWrapper merge(PropertyWrapper other) {
this.properties1.putAll(other.getProperties1());
this.properties2.putAll(other.getProperties2());
return this;
}
// getters and toString()
}
With that, the actual code might look like that:
public static void main(String[] args) {
Map<String, MyElement> sourceMap =
Map.of("key1", new MyElement(Map.of("a", new Property1("a"), "b", new Property1("b")), null),
"key2", new MyElement(null, Map.of("c", new Property2("c"), "d", new Property2("d"))));
PropertyWrapper result = sourceMap.values().stream()
.collect(
PropertyWrapper::new,
PropertyWrapper::merge,
PropertyWrapper::merge);
System.out.println(result.getProperties1());
System.out.println(result.getProperties2());
}
Output
{a=Property1{a}, b=Property1{b}}
{d=Property2{d}, c=Property2{c}}
Also note that it's a good practice to avoid keeping nullable references to collections. If these fields will always be initialized with empty collection, the need of null-check will be eliminated.
class MyObject {
int field;
public void setField(int arg1) {
this.field = arg1;
}
}
HashMap<String, MyObject> map;
...
... // put some MyObjects in the map with strings as keys
...
for (MyObject object : map.values()) {
object.setField(12345);
}
The changes I made to objects within the cycle are made on the same objects in the map?
The guide says this about the values() method
Returns a Collection view of the values contained in this map. The collection is backed by the map, so changes to the map are reflected in the collection, and vice-versa.
Does "changes to the map" mean "changes to the mapped objects"? So this way the setField method can change the objects in the map?
Does "changes to the map" mean "changes to the mapped objects"?
It means changes to the map (but see also 1 below). The collection is a live view of the values in the map, so as you add entries to the map or remove entries from the map, the collection reflects those changes; the two are linked. E.g.:
Map<String, String> m = new HashMap<String, String>();
Collection<String> c = m.values();
m.put("hi, "there");
System.out.println(c.size()); // 1, not 0
Live Example
1 Separately: Naturally changes to the state of objects stored as values in the map will be visible regardless of whether you get the reference to those objects via the collection or the map; they're references to the objects, not copies of the objects.
The method HashMap.values() - as described in the javadoc.
Returns a Collection view of the values contained in this map. The collection is backed by the map, so changes to the map are reflected in the collection, and vice-versa. If the map is modified while an iteration over the collection is in progress (except through the iterator's own remove operation), the results of the iteration are undefined. The collection supports element removal, which removes the corresponding mapping from the map, via the Iterator.remove, Collection.remove, removeAll, retainAll and clear operations. It does not support the add or addAll operations.
What this is saying is it returns a collection (similar to a List) of all the elements in the array. It also states that the collection is backed by the map, so if you change the map, the collection will also update, and changing the collection will also change the map. Note that it is impossible to add elements from this collection.
This example shows the use of the method quite well.
public static void main(String[] args) {
Map<String, String> mapValues = new HashMap<>();
mapValues.put("Hi", "Hello");
mapValues.put("Bye", "Goodbye");
System.out.println(mapValues.size());//prints 2
Collection<String> values = mapValues.values();
values.remove("Hello");
System.out.println(mapValues.size());//prints 1
System.out.println(values.size());//prints 1
mapValues.put("Morning", "Good morning");
System.out.println(mapValues.size());//prints 2
System.out.println(values.size());//prints 2
}
The values method returns a set of references to your objects in memory. Since your objects are mutable, any changes made to them will be reflected in the map, since the map has references to the same memory.
An example for Map that contains:
"key1" -> "value1"
"key2" -> "value2"
values() will return a collection of: "value1", "value2"
And yes, if you use a mutating method on an object in the map it will change in the values() collection that you retrieved previously.
But the most interesting part is that adding/removing elements from map will cause the values collection to change.
Example:
Map<String, String> map = new HashMap<>();
Collection<String> vals = map.values();
System.out.println("Before: " + vals);
map.put("key1", "value1");
System.out.println("After: " + vals);
This will print:
Before: []
After: [value1]
I have been trying to learn Java 8's new functional interface features, and I am having some difficulty refactoring code that I have previously written.
As part of a test case, I want to store a list of read names in a Map structure in order to check to see if those reads have been "fixed" in a subsequent section of code. I am converting from an existing Map> data structure. The reason why I am flattening this datastructure is because the outer "String" key of the original Map is not needed in the subsequent analysis (I used it to segregate data from different sources before merging them in the intermediate data). Here is my original program logic:
public class MyClass {
private Map<String, Map<String, Short>> anchorLookup;
...
public void CheckMissingAnchors(...){
Map<String, Boolean> anchorfound = new HashMap<>();
// My old logic used the foreach syntax to populate the "anchorfound" map
for(String rg : anchorLookup.keySet()){
for(String clone : anchorLookup.get(rg).keySet()){
anchorfound.put(clone, false);
}
}
...
// Does work to identify the read name in the file. If found, the boolean in the map
// is set to "true." Afterwards, the program prints the "true" and "false" counts in
// the map
}
}
I attempted to refactor the code to use functional interfaces; however, I getting errors from my IDE (Netbeans 8.0 Patch 2 running Java 1.8.0_05):
public class MyClass {
private Map<String, Map<String, Short>> anchorLookup;
...
public void CheckMissingAnchors(...){
Map<String, Boolean> anchorfound = anchorLookup.keySet()
.stream()
.map((s) -> anchorlookup.get(s).keySet()) // at this point I am expecting a
// Stream<Set<String>> which I thought could be "streamed" for the collector method
// ; however, my IDE does not allow me to select the "stream()" method
.sequential() // this still gives me a Stream<Set<String>>
.collect(Collectors.toMap((s) -> s, (s) -> false);
// I receive an error for the preceding method call, as Stream<Set<String>> cannot be
// converted to type String
...
}
}
Is there a better way to create the "anchorfound" map using the Collection methods or is the vanilla Java "foreach" structure the best way to generate this data structure?
I apologize for any obvious errors in my code. My formal training was not in computer science but I would like to learn more about Java's implementation of functional programming concepts.
I believe what you need is a flatMap.
This way you convert each key of the outer map to a stream of the keys of the corresponding inner map, and then flatten them to a single stream of String.
public class MyClass {
private Map<String, Map<String, Short>> anchorLookup;
...
public void CheckMissingAnchors(...){
Map<String, Boolean> anchorfound = anchorLookup.keySet()
.stream()
.flatMap(s -> anchorlookup.get(s).keySet().stream())
.collect(Collectors.toMap((s) -> s, (s) -> false);
...
}
}
Eran's suggestion of flatMap is a good one, +1.
This can be simplified somewhat by using Map.values() instead of Map.keySet(), since the map's keys aren't used for any other purpose than to retrieve the values. Streaming the result of Map.values() gives a Stream<Map<String,Short>>. Here we don't care about the inner map's values, so we can use keySet() to extract the keys, giving a Stream<Set<String>>. Now we just flatMap these sets into Stream<String>. Finally we send the results into the collector as before.
The resulting code looks like this:
public class MyClass {
private Map<String, Map<String, Short>> anchorLookup;
public void checkMissingAnchors() {
Map<String, Boolean> anchorfound = anchorLookup.values().stream()
.map(Map::keySet)
.flatMap(Set::stream)
.collect(Collectors.toMap(s -> s, s -> false));
}
}
Based on the following code snippet :
Hashtable balance = new Hashtable();
Enumeration names;
String str;
double bal;
balance.put("Zara", new Double(3434.34)); //first entry for Zara
balance.put("Mahnaz", new Double(123.22));
balance.put("Zara", new Double(1378.00)); //second entry for Zara
balance.put("Daisy", new Double(99.22));
balance.put("Qadir", new Double(-19.08));
System.out.println(balance.entrySet());
.
Output : [Qadir=-19.08, Mahnaz=123.22, Daisy=99.22, Zara=1378.0]
Why isn't chaining happening here? When I re-enter with Zara as key the old value is overwritten. I expected it to be added at the end of the Linked List at Zara".hashcode() index.
Does Java use separate chaining only for collision handling?
If I can't use chaining( as I'v tried above) please suggest a common method to do so.
Does Java use separate chaining only for collision handling?
Yes. You can only have one entry per key in a Hashtable (or HashMap, which is what you should probably be using - along with generics). It's a key/value map, not a key/multiple-values map. In the context of a hash table, the term "collision" is usually used for the situation where two unequal keys have the same hash code. They still need to be treated as different keys, so the implementation has to cope with that. That's not the situation you're in.
It sounds like you might want a multi-map, such as one of the ones in Guava. You can then ask a multimap for all values associated with a particular key.
EDIT: If you want to build your own sort of multimap, you'd have something like:
// Warning: completely untested
public final class Multimap<K, V> {
private final Map<K, List<V>> map = new HashMap<>();
public void add(K key, V value) {
List<V> list = map.get(key);
if (list == null) {
list = new ArrayList();
map.put(key, list);
}
list.add(value);
}
public Iterable<V> getValues(K key) {
List<V> list = map.get(key);
return list == null ? Collections.<V>emptyList()
: Collections.unmodifiableList(list);
}
}
Quote from the documentation of Map (which Hashtable is an implementation of):
An object that maps keys to values. A map cannot contain duplicate keys; each key can map to at most one value.
(emphasis mine)
The documentation of put() also says:
If the map previously contained a mapping for the key, the old value is replaced by the specified value
So if you want multiple values associated with a key, use a Map<String, List<Double>> instead of a Map<String, Double>. Guava also has a Multimap, which does what you want without having to deal with Lists explicitely as with a Map<String, List<Double>>.
This is a very basic question, I'm just not that good with Java. I have a Map and I want to get a list or something of the keys in sorted order so I can iterate over them.
Use a TreeMap, which is an implementation of the SortedMap interface. It presents its keys in sorted order.
Map<String, Object> map = new TreeMap<String, Object>();
/* Add entries to the map in any order. */
...
/* Now, iterate over the map's contents, sorted by key. */
for (Map.Entry<String, ?> entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
If you are working with another Map implementation that isn't sorted as you like, you can pass it to the constructor of TreeMap to create a new map with sorted keys.
void process(Map<String, Object> original) {
Map<String, Object> copy = new TreeMap<String, Object>(original);
/* Now use "copy", which will have keys in sorted order. */
...
}
A TreeMap works with any type of key that implements the Comparable interface, putting them in their "natural" order. For keys that aren't Comparable, or whose natural ordering isn't what you need, you can implement your own Comparator and specify that in the constructor.
You have several options. Listed in order of preference:
Use a SortedMap:
SortedMap<whatever> myNewMap = new TreeMap<whatever>(myOldMap);
This is vastly preferable if you want to iterate more than once. It keeps the keys sorted so you don't have to sort them before iterating.
There is no #2.
There is no #3, either.
SortedSet<whatever> keys = new TreeSet<whatever>(myMap.keySet());
List<whatever> keys = new ArrayList<whatever>(myMap.keySet());
Collections.sort(keys);
The last two will get you what you want, but should only be used if you only want to iterate once and then forget the whole thing.
You can create a sorted collection when iterating but it make more sense to have a sorted map in the first place. (As has already been suggested)
All the same, here is how you do it.
Map<String, Object> map;
for(String key: new TreeSet<String>(map.keySet()) {
// accessed in sorted order.
}
Apart from the methods mentioned in other answers, with Java 8 streams, another shorthand to get a sorted key list from a map would be -
List<T> sortedKeys = myMap.keySet().stream().sorted().collect(Collectors.toList());
One could actually get stuff done after .sorted() as well (like using a .map(...) or a .forEach(...)), instead of collecting it in the list and then iterating over the list.