Как я могу найти ключ на карте, основанный на сопоставлении шаблонов в 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"))
    }
  }

}