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");
Related
I have a ConcurrentHashMap that looks like this:
private Map<String,Map<String,Set<PublicKey>>> instancePairs = new ConcurrentHashMap<>();
And a method that is supposed to fill this hashmap up.
But i can't figure out how to put the values in the map
Currently i have:
instancePairs.putIfAbsent(inMemoryInstance.getUsername(), inMemoryInstance.getId() , publicKeySet);
Intellij Idea is giving me this error:
As mentioned by "DDovzhenko", you'd need to do something along the following lines.
//Get the map containing the ID as keys, if it doesn't exist, then create one.
Map mapValuesForName = instancePairs.getOrDefault(inMemoryInstance.getUsername(), new ConcurrentHashMap<String,Set<PublicKey>>());
//Put the publicKeySet based on the Id.
mapValuesForName.putIfAbsent(inMemoryInstance.getId(), publicKeySet);
//Store the potentially changed/new value back in original map.
instancePairs.put(mapValuesForName);
I am getting an exception from a Guava BiMap's putIfAbsent method in a multi-thread situation. How should I correctly protect it from threading problems?
I create the map like this:
BiMap<Integer, java.net.URI> cache = com.google.common.collect.Maps.synchronizedBiMap(HashBiMap.create());
Then, the only times I ever modify the map are by cache.clear(); or cache.putIfAbsent(a,b)
I have occasionally seen this stack trace:
java.lang.IllegalArgumentException: value already present: http://example.com
at com.google.common.collect.HashBiMap.put(HashBiMap.java:279)
at com.google.common.collect.HashBiMap.put(HashBiMap.java:260)
at java.util.Map.putIfAbsent(Map.java:744)
at com.google.common.collect.Synchronized$SynchronizedMap.putIfAbsent(Synchronized.java:1120)
Is this a bug in HashBiMap or synchronizedBiMap? Or do I need to do extra work for thread safety?
Using guava-25.0-jre and Java(TM) SE Runtime Environment 1.8.0_152-b16
Because a BiMap provides a mapping from values to keys, as well as the usual Map mapping from keys to values, each value can only be paired with a single key. Trying to associate a value with more than one unique key will result in an IllegalArgumentException that you are seeing.
It does not sounds like your issue is threading related, rather data related.
As a example, this will throw a similar exception. The problem is the presence of value "Bar" with two separate keys "Foo" and "Baz":
public static void main(String[] args) {
BiMap<String, String> m = HashBiMap.create();
m.put("Foo", "Bar");
m.put("Baz", "Bar"); // Throws IllegalArgumentException "value already present"
}
This doesn't have anything to do with synchronization, but it's how BiMap works. You can reproduce it easily:
cache.putIfAbsent(1, URI.create("http://example.com"));
cache.putIfAbsent(2, URI.create("http://stackoverflow.com"));
System.out.println(cache);
// {1=http://example.com, 2=http://stackoverflow.com}
cache.putIfAbsent(3, URI.create("http://example.com"));
// java.lang.IllegalArgumentException: value already present: http://example.com
BiMap is "a map that preserves the uniqueness of its values as well as that of its keys." This means that you can't put example.com again, even under different key. See also wiki page describing BiMap:
BiMap.put(key, value) will throw an IllegalArgumentException if you attempt to map a key to an already-present value. If you wish to delete any preexisting entry with the specified value, use BiMap.forcePut(key, value) instead.
In your case you could use forcePut and not fail with an exception:
cache.forcePut(3, URI.create("http://example.com"));
System.out.println(cache);
// {2=http://stackoverflow.com, 3=http://example.com}
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
lets say i have 2 arrays of the objects which are mapped to each other in the following schemna:
array1 :
String [] prog_types1 = {"Program1","Program2","Program3","Program4"};
and array2 :
String [] prog_types2 ={"SubProgram1","SubProgram2","SubProgram3","SubProgram4",
"SubProgram5","SubProgram6","SubProgram7","SubProgram8","SubProgram9","SubProgram10"};
as it understood from its names, prog_types2 is an extension for prog_types1, but has some repeated values, so the full mapping between these programs would looks liek this:
prog_types1 prog_types2
ProgramType1 SubProgramType1
ProgramType1 SubProgramType2
ProgramType1 SubProgramType7
ProgramType1 SubProgramType9
ProgramType2 SubProgramType12
ProgramType2 SubProgramType7
ProgramType2 SubProgramType9
ProgramType3 SubProgramType1
ProgramType3 SubProgramType2
ProgramType3 SubProgramType21
ProgramType3 SubProgramType27
ProgramType3 SubProgramType7
ProgramType5 SubProgramType12
ProgramType5 SubProgramType9
my question is : what is the best way to map these arrays to each other, from the perspective of faster processing and reuse?
I have implemented it as :
-- set of classes (class prog1 and prog2 and after put it into vector)...
-- hashtable with hashset
-- possible one more array
the way i am looking for should not consist of creating the same prog2 objects again for prog1 object, as it would be in all of the ways described earlier, but map it by the index position for example or in any other way.
just lookin for a nice algorythmical way to resolve it...
thanks in advance
p.s. it should be used within 1 package only between couple of classes and the main use of it would be a population of the prog2 types values based on the prog1 type value
p.s.2 java7
Using MultiMap from Guava Libraries, you could say:
Multimap<String, String> mmap = ArrayListMultimap.create();
mmap.put("Program1", "SubProgramType1");
mmap.put("Program1", "SubProgramType2");
// etc.
mmap.get("Program1")
would look like:
[SubProgramType1, SubProgramType2, SubProgramType7, SubProgramType9]
BTW, Hashtable is not used now for hashed collections, has been superceded by HashMap :)
IMO the best way would be a:
Map<String, List<String>> programs = new HashMap<String, List<String>>();
with the strings in the first list as keys and the corresponding subprograms composing the value list. Now the mapping is obvious:
ProgramType1 -> [SubProgramType1, SubProgramType2, SubProgramType7, SubProgramType9]
ProgramType2 -> [SubProgramType12, SubProgramType7, SubProgramType9]
ProgramType3 -> [SubProgramType1, SubProgramType2, SubProgramType21, SubProgramType27, SubProgramType7]
ProgramType5 -> [SubProgramType12, SubProgramType9]
Guava ListMultimap, that gives List<E>, not Collection<E> - little more pleasant.
private ListMultimap<String,Something> stuff = ArrayListMultimap.create();
// ...
public void add(String key, Something item) {
stuff.put(key, item);
}
public List<Something> get(String key) {
// might as well use the Lists convenience API while we're at it.
return Lists.newArrayList(stuff.get(key));
}
http://www.coffee-bytes.com/2011/12/22/guava-multimaps
btw, since i need :
-- separately use Program1 values
-- separately use SubProgram1 values
-- populate SubProgram1 values based on Program1 value
the easiest solution here would be to declare a double dimensional array with all the dublicates (as it dysplayed in full map schema) and for 1) and 2) populate data from it using non repeating algorythm and 3) loop cycle from 2nd dimension
so no reason to declare 3 objects, huge memory save and nice approach.
i am giving myself a star for it:)
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);
}