[英]How can I find a key in a map based on a pattern matching in java?
我想在具有模式匹配的地图中找到键。
Ex:-
Map<String, String> map = new HashMap<String, String>();
map.put("address1", "test test test");
map.put("address2", "aaaaaaaaaaa");
map.put("fullname", "bla bla");
从上面的地图中,我想获取前缀为“address”的键的值。 所以在这个例子中输出应该是前两个结果(“address1”和“address2”)。
我怎样才能动态地实现这一点?
您可以获取映射的keySet
,然后过滤以仅获取以“address”开头的键,并将有效键添加到新的 Set 中。
使用 Java 8,它就不那么冗长了:
Set<String> set = map.keySet()
.stream()
.filter(s -> s.startsWith("address"))
.collect(Collectors.toSet());
如果你有 Java 8 特性,这样的事情应该可以工作:
Set<String> addresses = map.entrySet()
.stream()
.filter(entry -> entry.getKey().startsWith("address"))
.map(Map.Entry::getValue)
.collect(Collectors.toSet());
像这样的东西:
for (Entry<String, String> entry : map.entrySet()) {
if (entry.getKey().startsWith("address")) {
// do stuff with entry
}
}
您将必须遍历密钥集并匹配模式
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.
}
我遇到了类似的需求,并尝试为这样的数据结构实现 POC。 我得出的结论是,以某种方式分区数据更实用:)
但是,如果您真的下定决心要实现类似的东西,您将需要一个更类似于特里树的结构。 这是我得到的(我很抱歉,因为代码在 Scala 中,但它可以很容易地进行调整,如果你下定决心,你可能可以完成它并使其可用)
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]) {
}
基本上这个想法是我们在随后的映射中查找每个字符,复杂性现在是键的长度。 这实际上应该是一个可以接受的限制,因为无论如何编译正则表达式很可能是 O(N)。 同样,如果您有较短的键并且许多条目会产生更好的性能,然后迭代所有键。 如果您将 mutable.HashMap 与某种自己的具有巧妙散列的实现交换,并利用字符实际上是 int 的事实,并且在 ASCII 字符串(可能是键)的情况下实际上是一个短的。 如果您要查找一些更复杂的表达式然后某事*,这也会更加困难,但仍然可能可行。
编辑:测试
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"))
}
}
}
我创建了一个界面...
import java.util.Map;
@FunctionalInterface
public interface MapLookup {
<V> List<V> lookup(String regularExpression, Map<String,V> map);
}
和实施
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<>();
}
}
考试
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);
}
结果
[-1, 3]
或者也许这太过分了。 不过,我可以看到重复使用它。
对于那些想要 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;
}
}
如果您不需要很大的性能,浏览地图上的所有键 ( map.entrySet
) 以获取与您的模式匹配的键就足够了。
如果您需要良好的性能,我用来解决此类问题的解决方案是使用内存数据库,例如 H2:将数据放在内存表中,在键上创建唯一索引,您将获得良好的性能对于 2 种情况:
select value from in_mem_table where key = ?'
),hashmap 的经典用法select value from in_mem_table where key like 'adress%'
)一种方法是创建一个函数,在所有地图中搜索以地址开头的键,但这会消除地图的优势,因为目标可能是快速。 另一种方法是创建一个包含所有以地址开头的键的列表或数组,但只有当您只想要以地址开头的键时才值得。
现在您是否需要能够搜索任何内容或仅搜索特定内容? 你需要地图还是可以是另一个像数组或列表这样的东西?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.