Je veux trouver des clés dans une carte avec un motif correspondant.
Ex:-
Map<String, String> map = new HashMap<String, String>();
map.put("address1", "test test test");
map.put("address2", "aaaaaaaaaaa");
map.put("fullname", "bla bla");
De la carte ci-dessus, je veux obtenir les valeurs des clés qui ont le préfixe "adresse". Ainsi, comme dans cet exemple, les deux premiers résultats ("adresse1" et "adresse2") doivent être générés.
Comment puis-je y parvenir de manière dynamique?
Merci.
Vous pouvez récupérer la keySet
de la carte, puis filtrer pour n’obtenir que les clés commençant par «adresse» et ajouter les clés valides à un nouvel ensemble.
Avec Java 8, c'est un peu moins bavard:
Set<String> set = map.keySet()
.stream()
.filter(s -> s.startsWith("address"))
.collect(Collectors.toSet());
Si vous avez des fonctionnalités Java 8, cela devrait fonctionner:
Set<String> addresses = map.entrySet()
.stream()
.filter(entry -> entry.getKey().startsWith("address"))
.map(Map.Entry::getValue)
.collect(Collectors.toSet());
Quelque chose comme ça:
for (Entry<String, String> entry : map.entrySet()) {
if (entry.getKey().startsWith("address")) {
// do stuff with entry
}
}
Vous devrez parcourir le jeu de clés et faire correspondre le motif
for(String key : map.keySet()) {
if(! key.startsWith("address")) {
continue;
}
// do whatever you want do as key will be match pattern to reach this code.
}
J'ai créé une interface ...
import Java.util.Map;
@FunctionalInterface
public interface MapLookup {
<V> List<V> lookup(String regularExpression, Map<String,V> map);
}
Et la mise en place
import Java.util.ArrayList;
import Java.util.List;
import Java.util.Map;
import Java.util.regex.Pattern;
import Java.util.stream.Collectors;
public class MapLookupImpl implements MapLookup {
@Override
public <V> List<V> lookup(String regularExpression, Map<String, V> map) {
final Pattern pattern = Pattern.compile(regularExpression);
List<String> values = map.keySet()
.stream()
.filter(string -> pattern.matcher(string).matches())
.collect(Collectors.toList());
if(values!= null && !values.isEmpty()){
return values.stream().map((key) -> map.get(key)).collect(Collectors.toList());
}
return new ArrayList<>();
}
}
Le test
public static void main(String[] args){
Map<String, Integer> map = new HashMap<>();
map.put("foo",3);
map.put("bar",42);
map.put("foobar",-1);
MapLookup lookup = new MapLookupImpl();
List<Integer> values = lookup.lookup("\\woo\\w*",map);
System.out.println(values);
}
Le résultat
[-1, 3]
Ou peut-être que c'est exagéré. Je peux voir une utilisation répétée pour cela, cependant.
Pour ceux qui veulent la version antérieure à Java8:
public class PreJava8MapLookup implements MapLookup {
@Override
public <V> List<V> lookup(String regularExpression, Map<String, V> map) {
Matcher matcher = Pattern.compile(regularExpression).matcher("");
Iterator<String> iterator = map.keySet().iterator();
List<V> values = new ArrayList<>();
while(iterator.hasNext()){
String key = iterator.next();
if(matcher.reset(key).matches()){
values.add(map.get(key));
}
}
return values;
}
}
Je suis tombé sur un besoin similaire et j'ai tenté de mettre en place un POC pour une telle structure de données. Je suis arrivé à la conclusion qu'il est beaucoup plus pratique de partitionner les données d'une manière ou d'une autre :)
Cependant, si vous avez vraiment l’intention de mettre en place une telle solution, vous aurez besoin d’une structure plus semblable à un arbre à trois. Voici ce que j’ai eu (mes excuses car le code est en Scala, mais il peut être facilement adapté et si vous le souhaitez, vous pouvez probablement le terminer et le rendre utilisable)
package component.datastructure
import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
class RegExpLookup[T] {
private val root = new mutable.HashMap[Char, Node]
def put(key: String, value: T): Unit = {
addNode(key.toCharArray, 0, root, value)
println(root.toString)
}
private def addNode(key: Array[Char], charIdx: Int,
currentRoot: mutable.Map[Char, Node], value: T): Unit = {
if (charIdx < key.length - 1) {
if (currentRoot.contains(key(charIdx))) {
addNode(key, charIdx + 1, currentRoot(key(charIdx)).nodeRoot, value)
} else {
val node = Node(null, new mutable.HashMap[Char, Node])
currentRoot.put(key(charIdx), node)
addNode(key, charIdx + 1, node.nodeRoot, value)
}
} else {
currentRoot.put(key(charIdx), Node(value, null))
}
}
private def getAll(lastNode: Node, buffer: ArrayBuffer[T]): Unit = {
if (lastNode.value != null)
buffer.append(lastNode.value.asInstanceOf[T])
if (lastNode.nodeRoot != null)
lastNode.nodeRoot.values.foreach(e => {
getAll(e, buffer)
})
}
def get(key: String): Iterable[T] = {
val t = findLastNode(key.toCharArray, 0, root)
println("getting from " + root)
val isLast = t._2
if (isLast) {
val v = t._1.value
if (v != null)
return List(v.asInstanceOf[T])
else
return null
} else {
val buffer = new ArrayBuffer[T]()
getAll(t._1, buffer)
return buffer.toList
}
}
private def findLastNode(key: Array[Char], charIdx: Int,
root: mutable.Map[Char, Node]): (Node, Boolean) = {
if (charIdx < key.length - 2 && (key(charIdx + 1) != '*')) {
return (root(key(charIdx)), false)
} else if (charIdx < key.length - 1) {
return findLastNode(key, charIdx + 1, root(key(charIdx)).nodeRoot)
} else
return (root(key(charIdx)), true)
}
}
case class Node(value: Any, private[datastructure] val nodeRoot: mutable.HashMap[Char, Node]) {
}
Fondamentalement, l’idée est que nous examinons chaque caractère d’une carte ultérieure, la complexité étant désormais la longueur de la clé. Ce qui, en réalité, devrait constituer une limite acceptable, étant donné que la compilation d’un enregistrement est probablement O(N) de toute façon. De même, dans le cas où vous avez des clés plus courtes et que de nombreuses entrées donnent de bien meilleures performances que de parcourir toutes les clés. Si vous permutez le mutable.HashMap avec une sorte d'implémentation propre avec un hachage intelligent et tirez parti du fait qu'un caractère est vraiment un int, et dans le cas où ASCII (ce sera probablement la clé) en fait un court. Ce serait également plus difficile si vous recherchiez une expression plus complexe que quelque chose *, mais qui reste probablement faisable.
edit: un test
class MySpec extends PlaySpec {
val map = new RegExpLookup[String]()
"RegExpLookup" should {
"put a bunch of values and get all matching ones" in {
map.put("abc1", "123")
map.put("abc2", "456")
map.put("abc3", "789")
val result = map.get("abc*")
println(result)
val s = result.toSet
assert(s.contains("123"))
assert(s.contains("456"))
assert(s.contains("789"))
}
"put a single value and get it by exact key" in {
map.put("abc", "xyz")
val result = map.get("abc")
println(result)
assert(result.head.equals("xyz"))
}
}
}
Si vous n'avez pas besoin de grandes performances, parcourir toutes les touches de votre carte (map.entrySet
) pour obtenir celles qui correspondent à votre modèle devrait suffire.
Si vous avez besoin de bonnes performances, une solution que j'ai utilisée pour résoudre ce type de problème consiste à utiliser une base de données en mémoire telle que H2: vous mettez vos données dans une table mémoire, créez un index unique sur la clé et obtenez de bonnes performances. pour les 2 cas:
select value from in_mem_table where key = ?'
), utilisation classique d'un hashmapselect value from in_mem_table where key like 'adress%'
) Une solution consiste à créer une fonction qui recherche dans toute la carte les clés commençant par adresse, mais qui supprime l’avantage de la carte car l’objectif est probablement d’être rapide . Une autre méthode consiste à créer une liste ou un tableau contenant clés commençant par adresse, mais cela ne vaut que si vous voulez juste les clés commençant par adresse.
Maintenant, avez-vous besoin de pouvoir rechercher quelque chose ou juste un objet spécifique? Et avez-vous besoin de la carte ou cela peut-il être autre chose qu'un tableau ou une liste?