Как я могу найти ключ на карте, основанный на сопоставлении шаблонов в 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");
Из вышеприведенной карты я хочу получить значения ключей с префиксом "адрес". Так как в этом примере вывод должен быть первым двумя результатами ( "address1" и "address2" ).
Как я могу достичь этого динамически?
Спасибо.
Ответы
Ответ 1
Вы можете захватить keySet
на карте, а затем отфильтровать, чтобы получить только ключи, которые начинаются с "адреса" и добавить действительные ключи к новому набору.
С Java 8 это немного менее многословно:
Set<String> set = map.keySet()
.stream()
.filter(s -> s.startsWith("address"))
.collect(Collectors.toSet());
Ответ 2
Если у вас есть функции Java 8, что-то вроде этого должно работать:
Set<String> addresses = map.entrySet()
.stream()
.filter(entry -> entry.getKey().startsWith("address"))
.map(Map.Entry::getValue)
.collect(Collectors.toSet());
Ответ 3
Что-то вроде этого:
for (Entry<String, String> entry : map.entrySet()) {
if (entry.getKey().startsWith("address")) {
// do stuff with entry
}
}
Ответ 4
Вам нужно будет пройти через набор ключей и сопоставить шаблон
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.
}
Ответ 5
Я создал интерфейс...
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]
Или, может быть, это перебор. Однако я вижу повторное использование этого.
Для тех, кто хочет версию pre-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;
}
}
Ответ 6
Если вам не нужна большая производительность, просмотр всех ключей на вашей карте (map.entrySet
), чтобы получить те, которые соответствуют вашему шаблону, должно быть достаточно.
Если вам нужна хорошая производительность, решение, которое я использовал для решения этой проблемы, заключается в использовании базы данных в памяти, такой как H2: вы помещаете свои данные в таблицу памяти, создаете уникальный ключ на ключе, и вы будете получить хорошую производительность для двух случаев:
- Получение значения, связанного с ключом (
select value from in_mem_table where key = ?'
), классическое использование hashmap
- Получение значений, связанных с "шаблоном ключа" (
select value from in_mem_table where key like 'adress%'
)
Ответ 7
Один из способов - создать функцию, которая ищет всю карту для ключей, начинающихся с адреса, но это позволит удалить преимущество карты, поскольку цель, вероятно, будет быстрой.
Другой способ - создать список или массив, содержащий все ключи, начинающиеся с адреса, но это стоит только, если вы просто хотите, чтобы ключи начинались с адреса.
Теперь вам нужно искать что-либо или только конкретную вещь? И вам нужна карта, или это может быть другая вещь, например, массив или список?
Ответ 8
Я столкнулся с подобной потребностью и попытался реализовать POC для такой структуры данных. Я пришел к выводу, что гораздо более практично делить данные каким-то образом:)
Однако, если вы действительно настроитесь на реализацию чего-то подобного, вам понадобится структура, более похожая на дерево trie. Вот что я получил (мои извинения, так как код находится в 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]) {
}
В основном идея состоит в том, что мы просматриваем каждого персонажа на последующей карте, теперь сложностью будет длина ключа. Который, действительно, должен быть приемлемым ограничением, поскольку компиляция reg ex, вероятно, 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"))
}
}
}