In Java I have a java.util.Properties object and I want to obtain another one with the same pairs but keys converted to values and viceversa.
If there are collision (i.e. there are two equal values) then just pick as value an arbitrary key.
What is the shortest way to do it?
Feel free to use libraries, commons-collections, or whatever.
You can consider using a BiMap by google collections which is essentially a reversable Map. It guarantees uniquness of keys as well as values.
Check it out here. This is the API
A Properties object is a Hashtable object, so you should be able to do something like:
Hashtable<String, String> reversedProps = new Hashtable<String, String>();
for (String key : props.keySet()) {
reversedProps.put(props.get(key), key);
}
Result: 3 lines of code.
This code is untested, but it should give you the idea.
Something like:
Properties fowards = new Properties();
fowards.load(new FileInputStream("local.properties"));
Properties backwards = new Properties();
for (String propertyName : fowards.stringPropertyNames())
{
backwards.setProperty(forwards.get(propertyName), propertyName);
}
Related
I welcome methods in the API to easily create default initialisations.
For example in HashMaps. But why have they not been provided with Supplier Lambda methods? - Or am I missing an important step, or did I not learn the latest java Api versions?
Standard (Java8) version:
Map<String,List<Integer>> datas = new HashMap<>();
List<Integer> integersList = datas.getOrDefault( "somekey", new ArrayList<>() );
which would instantiate a new ArrayList anytime the code is executed - no matter if the new list is needed or not.
Desired Lambda supplier version:
Map<String,List<Integer>> datas = new HashMap<>();
List<Integer> integersList = datas.getOrDefault( "somekey", ()->new ArrayList() );
Would instantiate (or execute some instantiation code) only in case demanded key is not within the map.
The code of the getOrDefault()-Method could look something like this:
public V getOrDefault( K key, Supplier<V> supplier ) {
if ( !super.containsKey( key ) && supplier != null ) {
super.put( key, supplier.get() );
}
return super.get( key );
}
Why did they(?) not build it that way initially or added such functionality later on?
I guess there is even more examples where Lambda would solve an unnecessary code execution - not just Maps as shown with this example.
By the way: sorry for re-asking a question but I would not know how to exactly look for my question with different terms...
Be welcome to post helpful links.
Thanks for your shared knowledge :-)
What you are looking for exists since Java 8. Take a look at the javadoc of the HashMap and specifically the method Hashmap.computeIfAbsent. This method allows for adding new entries to the HashMap if none can be found using the key provided.
Examaple:
Map<Integer, String> map = new HashMap();
String created = map.computeIfAbsent(1, k -> "Test");
System.out.println(created);
The code above will trigger the HashMap to call the provided Function to add a new entry since it cannot find an existing one. It both returns the new entry and call the Hashmap.put method to add it.
I am not sure how to define the key for the message size of my KafkaSpouts.
My example:
Map<String, Object> props = new HashMap<>();
props.put("fetch.message.max.bytes", "2097152"); // 2MB
props.put(KafkaSpoutConfig.Consumer.GROUP_ID, group);
I searched for the constant key definition of "fetch.message.max.bytes" without succeed.
I expect this key in KafkaSpoutConfig.Consumer or at least KafkaSpoutConfig.
Anyone know the correct location?
Storm's KafkaSpout does not offer all available keys as perdefined members. However, if you know the name of the key, you can safely use a String (as shown in your example) of use a Kafka class that defines the key.
I am reading a simple JSON....
{"A":0,"B":0,"C":2,"D":0,"F":5}
into a map using JsonSlurper in Groovy...
Map gradeDistributon = jsonSlurper.parseText(jsonString)
But when iterating over this map with a closure..
gradeDistributon.each{ entry ->
println "From map got key ${entry.key}"
I am seeing the keys are not in the order they were in the original JSON, for example 'C' comes first. I think this is because Map does not maintain insertion order in Java. Is there a way I can keep the order of the original JSON?
If it means reading the JSON in a different way (instead of into a Map with JsonSlurper) then I am fine with that if you can show me how.
You can set JVM system property jdk.map.althashing.threshold to make JsonSlurper to use a LinkedHashMap instead of TreeMap as the internal Map implementation, e.g. -Djdk.map.althashing.threshold=512.
The reason is in source code of groovy.json.internal.LazyMap used by JsonSlurper.
private static final String JDK_MAP_ALTHASHING_SYSPROP = System.getProperty("jdk.map.althashing.threshold");
private void buildIfNeeded() {
if (map == null) {
/** added to avoid hash collision attack. */
if (Sys.is1_7OrLater() && JDK_MAP_ALTHASHING_SYSPROP != null) {
map = new LinkedHashMap<String, Object>(size, 0.01f);
} else {
map = new TreeMap<String, Object>();
}
}
}
Please note this solution should be used as a hack as it depends on Groovy's internal implementation details. So this behavior may change in future version of Groovy.
See my blog post for details.
So it was just a matter of sorting the keys after JsonSlurper built the Map, for that I just read into a TreeMap which sorts the keys by default..
TreeMap gradeDistributon = jsonSlurper.parseText(jsonString)
I can't reproduce your behaviour with groovy 2.4.5 but you can try using LinkedHashMap (allow to iterate over map keys maintaining the order in which the entries were inserted):
import groovy.json.*
def jsonText = '''
{"A":0,"B":0,"C":2,"D":0,"F":5,"G":7,"H":9}
'''
LinkedHashMap json = new JsonSlurper().parseText(jsonText)
json.each{ entry ->
println "${entry.key}"
}
NOTE: as stated by #XenoN the JsonSlurper() sort the json keys during the parsing process so independently of the input order (ie. {"H":0,"B":0,"A":2,"D":0,"G":5,"F":7,"C":9}) the output of JsonSlurper will be always: {"A":2,"B":0,"C":9,"D":0,"F":7,"G":5,"H":0}.
Using the LinkedHashMap instead of a HashMap we preserve the order given by JsonSlurper.
I run the same code on Groovy 2.4.x and on 3.0.x.
On 2.4 the order is preserved,but on 3.0 is sorted asc by default.
use the JsonSluperClassic().parse() instead it will preserve the order
We can iterate through collection easily by pressing Ctrl+Alt+T,
And then I wanted to create such template for iterating through map:
I wrote these lines to template text box:
for (Map.Entry<$ELEMENT_TYPE$> $VAR$ : $SELECTION$.entrySet()) {
$END$
}
Now it is generating these codes:
HashMap<String,Object> map=new HashMap<String,Object>();
for (Map.Entry<Object> objectEntry : map.entrySet()) {
}
Map.Entry<Object> should be Map.Entry<String,Object>. I cannot find a way to introduce variable correctly. How can I do that?
It is easier if you just type iter and then Tab.
You will get a drop-down and there you can choose map.entrySet() and it will give you:
for (Map.Entry<String, Object> stringObjectEntry : map.entrySet()) {
}
To view a list of live template available: Ctrl + J and then Tab.
From there you will have list of live template, iter (for each loop) will be on the list.
I wrote this, it works in 1 step. I also like having the key and value in there already, but those can be omitted:
for (Map.Entry<$KEY_TYPE$, $VALUE_TYPE$> $PAIR$ : $ITERABLE_TYPE$.entrySet()) {
$KEY_TYPE$ key = $PAIR$.getKey();
$VALUE_TYPE$ value = $PAIR$.getValue();
$END$
}
Variables:
KEY_TYPE: guessElementType(iterableVariable())
VALUE_TYPE: regularExpression(typeOfVariable(ITERABLE_TYPE),"^.*,(.*)>$", "$1")
PAIR: suggestVariableName()
ITERABLE_TYPE: variableOfType("java.util.Map")
The large amount of variables and functions make the Live Templates a powerful tool. In VALUE_TYPE, I am using a regular expression to get the ValueType out of Map<KeyType, ValueType>. I tried a lot of "cleaner" approaches, but none worked.
Keys are a file and a word. The file gives all words inside the file. The word gives all files having the word. I am unsure of the domain and co-domain parts. I want K to be of the type <String> and V to be of type <HashSet<FileObject>>.
public HashBiMap<K<String>,V<HashSet<FileObject>>> wordToFiles
= new HashBiMap<K<String>,V<HashSet<FileObject>>>();
public HashBiMap<K<String>,V<HashSet<FileObject>>> fileToWords
= new HashBiMap<K<String>,V<HashSet<FileObject>>>();
Google's HashBiMap.
change it to
public HashBiMap<String,HashSet<FileObject>> wordToFiles = HashBiMap.create ();
But still it looks very strange. I think you should use another collection. From BiMap documentation (HashBiMap impelements BiMap):
A bimap (or "bidirectional map") is a
map that preserves the uniqueness of
its values as well as that of its
keys. This constraint enables bimaps
to support an "inverse view", which is
another bimap containing the same
entries as this bimap but with
reversed keys and values.
I don't know the problem you want to solve but after looking at your code I can suggest to consider using Multimaps. From its docs:
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.
For example, you can do something like this:
Multimap<String, FileObject> wordToFiles = HashMultimap.create();
wordToFiles.put("first", somefile);
wordToFiles.put("first", anotherfile);
for (FileObject file : wordToFiles.get("first"){
doSomethingWithFile (file);
}
Add this dependency to your 'build.gradle'
compile 'com.google.guava:guava:19.0'
import BiMap and HashBiMap
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
Create a bimap
BiMap<String, String> myBiMap = HashBiMap.create();
Put some values
myBiMap.put("key", "value");
Get mapping value by key,
myBiMap.get("key");
Get mapping by value,
myBiMap.inverse().get("value");