Previously I had the TreeMap that is sorted via:
Map<String, Set<Stuff>> map = new TreeMap<>(String::compareToIgnoreCase);
I'm thinking of adding a special case. If the string is "Front", I want to ignore this ordering and just have it in the beginning of the map.
Map<String, Set<Stuff>> map = new TreeMap<>((s1, s2) -> {
//do a check to see if the string is "Front", otherwise use the above
});
This feels a bit convoluted to me... Is there a simpler way to do this?
Related
I have a Map dataset, and I want to iterate through the keys and search for matches.
So I want to find the maps element, where the key fits to this pattern:
String searchedKey = "A?C"; // ? means it can be any character
Map<String, MyObject> myMap = new HashMap<>();
myMap.put("ABC", MyObject(1));
myMap.put("CDF", MyObject(2));
myMap.put("ADS", MyObject(3));
for (Map.Entry<String,MyObject> entry : myMap.entrySet()) {
// in this case, I want to find the first element, because it's key fits the searchedKey, where ? can be anything
}
How can I do this?
Thanks!
You could do something like this to return a list of found MyObjects. Note I changed ? to . for any character.
String searchedKey = "A.C"; // ? means it can be any character
Map<String, MyObject> myMap = new HashMap<>();
myMap.put("ABC", new MyObject(1));
myMap.put("CDF", new MyObject(2));
myMap.put("ARS", new MyObject(3));
myMap.put("VS", new MyObject(4));
myMap.put("AQC", new MyObject(3));
myMap.put("DS", new MyObject(3));
myMap.put("ASC", new MyObject(10));
List<Map.Entry<String,MyObject>> list = myMap.entrySet().stream()
.filter(e -> e.getKey().matches(searchedKey))
.collect(Collectors.toList());
list.forEach(System.out::println);
Prints
ASC=10
ABC=1
AQC=3
The MyObject class
class MyObject {
int val;
public MyObject(int v) {
this.val = v;
}
public String toString() {
return val + "";
}
}
You could use Regex-Patterns that allow to search Strings for matchings of a logical sequence using String#matches(String).
Here is a page that might help you create and test a regex for your needs. You might also have to construct your pattern flexible during runtime, depending on how your search works.
Tho keep in mind that a HashMap does not keep the order in which the keys were inserted. keySet() does not return them in a fixed order. If you need them ordered, you could use a LinkedHashMap
Is there any fast and reliable way/approach to remove a set of entries based on an attribute on the entry's value. Below approach loops every entry which in undesirable.
e.g: ConcurrentHashMap - Entries will be in millions
Iterator<Map.Entry<String, POJO>> iterator = map.entrySet().iterator();
for (Iterator<Map.Entry<String, POJO>> it = iterator; it.hasNext(); ) {
Map.Entry<String, POJO> entry = it.next();
POJO value = entry.getValue();
if (value != null && *attribute*.equalsIgnoreCase(value.getAttribute())) {
it.remove();
}
}
There is no better way of mutating map in place without utilizing additional data structures. What you are basically asking for is secondary index like employed in databases, where you have pointers to entries based on some non-primary key properties. If you don't want to store extra index, there is no easier way then to iterate through all entries.
What I would suggest you to look into is composing map view over your original map. For example something like (using guava)
Map<String,POJO> smallerMap = Maps.filterValues(map,
v -> !attribute.equalsIgnoreCase(v.getAttribute())
);
You will need to be careful with such view (don't call size() on it for example), but for access etc it should be fine (depending on your exact needs, memory constraints etc).
And side note - please note I have removed null check for value. You cannot store null values in ConcurrentHashMap - and it is also not that great idea in normal map as well, better to remove entire key.
There are two solutions that I can think of
First solution:
Create another map for storing the object hashcode as key and corresponding key as value. The structure could be like as below
Map<Integer, String> map2 = new HashMap<>();
Here is working solution. Only drawback of this is getting a unique hashcode might be tough if there are large number of objects.
import java.util.HashMap;
import java.util.Map;
public class DualHashMap {
public static void main(String a[]){
Map<String, Pojo> map = new HashMap<>();
Map<Integer, String> map2 = new HashMap<>();
Pojo object1 = new Pojo();
Pojo object2 = new Pojo();
map.put( "key1", object1);
map.put( "key2", object2);
map2.put(object1.hashCode(), "key1");
map2.put(object2.hashCode(), "key2");
// Now let say you have to delete object1 you can do as follow
map.remove(map2.get(object1.hashCode()));
}
}
class Pojo{
#Override
public int hashCode(){
return super.hashCode(); //You must work on this yourself, and make sure hashcode is unique for each object
}
}
2nd solution:-
Use the solution provided for dual hashmap by Guava or Apache
The Apache Commons class you need is BidiMap.
Here is another for you by Guava
Any way to perform the below code using Java 8.
final Map<String, Collection<ProductStrAttributeOverrideRulesModel>> attributeRulesMap = new HashMap<String, Collection<ProductStrAttributeOverrideRulesModel>>();
for (final ProductStrAttributeOverrideRulesModel rule : rules)
{
final String key = rule.getProductStrAttributeOverride().getProductStrTypeField().getAttributeDescriptorQualifier();
if (attributeRulesMap.containsKey(key))
{
final Collection<ProductStrAttributeOverrideRulesModel> currentRules = attributeRulesMap.get(key);
currentRules.add(rule);
}
else
{
final Collection<ProductStrAttributeOverrideRulesModel> list = new LinkedList<ProductStrAttributeOverrideRulesModel>();
list.add(rule);
attributeRulesMap.put(key, list);
}
}
if it is only
final Map<String, ProductStrAttributeOverrideRulesModel> attributeRulesMap
than i can do like following but i need to arrange the whole collection inside a map based on key and each key can have multiple values stored in collection.
Map<String, ProductStrAttributeOverrideRulesModel> result =
choices.stream().collect(Collectors.toMap(ProductStrAttributeOverrideRulesModel::getProductStrAttributeOverride.getProductStrTypeField.getAttributeDescriptorQualifier,
Function.identity()));
You could use groupingBy :
Map<String,List<ProductStrAttributeOverrideRulesModel>>
map =
choices.stream()
.collect(Collectors.groupingBy(rule -> rule.getProductStrAttributeOverride().getProductStrTypeField().getAttributeDescriptorQualifier()));
And if you don't want a List, you can pass a second argument to groupingBy and specify whatever Collection you want. For example :
Map<String,Collection<ProductStrAttributeOverrideRulesModel>>
map =
choices.stream()
.collect(Collectors.groupingBy(rule -> rule.getProductStrAttributeOverride().getProductStrTypeField().getAttributeDescriptorQualifier(),
Collectors.toCollection(HashSet::new)));
Note that it doesn’t always have to be a Stream operation. Your code would also benefit from using the “diamond operator” (though not new to Java 8) and from using new collection operations, i.e. computeIfAbsent, which allows to elide the entire conditional inside the loop and its code duplication. Putting both together, you’ll get:
final Map<String, Collection<ProductStrAttributeOverrideRulesModel>>
attributeRulesMap = new HashMap<>();
for(final ProductStrAttributeOverrideRulesModel rule: rules)
{
final String key = rule.getProductStrAttributeOverride()
.getProductStrTypeField().getAttributeDescriptorQualifier();
attributeRulesMap.computeIfAbsent(key, x->new LinkedList<>()).add(rule);
}
You could also replace the loop by a forEach invocation, if you wish:
rules.forEach(rule -> attributeRulesMap.computeIfAbsent(
rule.getProductStrAttributeOverride()
.getProductStrTypeField().getAttributeDescriptorQualifier(),
x->new LinkedList<>()).add(rule)
);
though it’s debatable whether this is an improvement over the classical loop here…
If I have a HashMap<String,Object> that has three entries that are <String,String> and one that is <String,Integer> is there a way to "cast" this into a HashMap<String,String> easily? Right now I and making a new HashMap and copying all of the values over so that I can convert the Integer during the copying.
Should be able to cast it, but expect an exception when trying to retrieve one of the integers out of the map.
What I think you're asking is to cast the content of the hashmap, in this case the values, so they all come out as strings, and that's not going to happen.
Best solution is to convert the integer into a string when populating the map in the first place.
You can cast the original map, no need to copy:
map.put("intKey", map.get("intKey").toString());
Map<String, String> fixedMap = (Map) map;
It does look like something is wrong with your design though. It would be better to fix the code that shoves the integer into the map to begin with, so that you would not need to deal with this trickery downstream.
Something like this should be easier:
Class<? extends Object> className = obj.getClass();
if (className.getName().contains("String")){
//add the object
}else {
//convert it to String and then add to HashMap
}
If you're using java 8, you can use forEach with BiConsumer.
Take a look in the code below
Map<String, Object> map = new HashMap<>();
map.put("a", "a");
map.put("b", "b");
map.put("1", Integer.valueOf(1));
map.forEach((k, v) -> map.put(k, v.toString()));
After line map.forEach((k, v) -> map.put(k, v.toString())); all values in the map are Strings. Of courste that loop is still there, but you are using a language feature/resource.
When I put a (KEY, VALUE) into a map such as Map<String, List<String>>, and I want to check if the KEY is existed first to decide if I have to make a new List, usually My Java Code looks like this:
Map<String, List<String>> example = new HashMap<>();
public void put(String k, String v){
if(example.containsKey(k)){
example.get(k).add(v);
return;
}
List<String> vs = new ArrayList<>();
vs.add(v);
example.put(k,vs);
}
It doesn't looks very nice. Is there any way to make it more simple and more beautiful?
If you have Java 8 you can write this as one line:
example.computeIfAbsent(k, key -> new ArrayList<>()).add(v);
This uses a lambda, so the new ArrayList is only created if required.
(k and key need to have different names, as they are different variables)
Map<String, List<String>> example = new HashMap<>();
public void put(String k, String v){
if (!example.containsKey(k)){
example.put(k, new ArrayList<>();
}
example.get(k).add(v);
}
Arguably, this is slightly wasteful - requiring you to get the list you just put - but to my eye it is much cleaner and more expressive.
If you can't use other libraries, or java 8, you could wrap the whole map and construct in a class of you own.
With your own class you:
Confine the messiness to one place.
Hide the face you are using a Map behind the scenes.
Have a place to move any additional logic to.