【问题标题】:Regular Expression Map Implementation in Java [closed]Java中的正则表达式映射实现[关闭]
【发布时间】:2017-06-07 15:23:25
【问题描述】:

是否有任何键是正则表达式模式的 Map 的 Java 实现。这种数据结构有可能吗? 找到了几个手工解决方案,例如 https://wiki.apache.org/jakarta/RegexpKeyedMap,但我想要一个经过良好测试的库。

作为客户我想要这个

map.put('^RESOURCE.*', () -> { /* some action*/});
map.put('USER|ACCOUNT', () -> { /* do action*/}};
map.put('PRODUCT', () -> { /* do action */}};

例如

String entityType = 'RESOURCE_TYPE_1';
.....
Supplier<MyBatisMapper> action = mapers.get(entityType)
MyBatisMapper mapper = action.get();
mapper.update(entity);

它将用作静态配置。所以删除功能不是必需的。

已编辑

目前我们的项目中有几个大型交换机,任务是使条件更加复杂。喜欢(在伪代码中):

switch (type) {
    case 'USER' || 'ACCOUNT' : doAction();
    case startsWith('RESOURCE'): doAnotherAction();
    ...
   /* another 10-15 cases */
}

我怀疑什么是最佳方法。有几个想法,但没有一个看起来不错:

  • 用 if/else 改变开关
  • 建议创建一些 RegEx 容器,例如 dimo414
  • 为配置创建一些类。这个问题看起来太复杂了。

我用 groovy 很容易地解决了这个问题,

def configuration = [
  [matcher: { it.startsWith('RESOURCE' }, action: { /* */}],
  [matcher: { it == 'USER' || it == 'ACCOUNT' }, action: { /* */}]
]
...
def result = configuration.find({ it.matcher(type)}).action();

但是对于 Java,这样的解决方案会太脏(由于类型转换)。

【问题讨论】:

  • 要求我们推荐或查找书籍、工具、软件库、教程或其他非现场资源的问题对于 Stack Overflow 来说是题外话,因为它们往往会吸引固执己见的答案和垃圾邮件。
  • 我肯定会避免使用 RegexpKeyedMap 类型 - 它是 Java-6 之前的语法并且没有正确实现(例如,它没有实现 containsKey()putAll())。
  • "但是对于 Java,这样的解决方案会太脏(由于类型转换)" 我不明白你的意思,哪里是转换问题?使用RegexLookup 你会说configuration.putPattern("^RESOURCE.*", () -&gt; ...); 等等,然后使用configuration.find(someString); 再次返回操作。您对V 类型有疑问吗?使用Functionjava.util.function 中的其他类型之一。

标签: java regex dictionary data-structures


【解决方案1】:

Map 有一个相当复杂的合约,很难(或不可能)正确地遵循您所描述的数据结构。例如,没有有意义的方法来实现.entrySet(),因为有效的密钥是无限的。此外,此“地图”的行为与 Map 的概念目的并不真正匹配,因为查找成本很高(可能 O(nk),其中 k是模式的复杂度)。

我建议避免实现Map,而是定义一个仅支持您需要的行为的专用类。这可能看起来像:

/**
 * Associates a series of regular expressions with values, allowing the values
 * to be looked up by strings that match a pattern.
 *
 * Note this is a linear-time operation, and that patterns are checked in
 * insertion order.
 */
public class RegexLookup<V> {
  // Pattern doesn't override .equals()/.hashCode(), so it can't be the map key
  // use a LinkedHashMap to ensure ordered search
  private final LinkedHashMap<String, Pattern> patterns = new HashMap<>();
  private final HashMap<String, V> values = new HashMap<>();

  /** Associates a regular expression with a value */
  public void putPattern(String regex, V value) {
    putPattern(Pattern.compile(regex), regex);
  }

  /** Associates a regular expression with a value */
  public void putPattern(Pattern pattern, V value) {
    patterns.put(pattern.pattern(), pattern);
    values.put(pattern.pattern(), value);
  }

  /**
   * Looks for a pattern matching the given string, and returns the associated
   * value. If not match is found, returns {@link Optional#absent}.
   */
  public Optional<V> find(String string) {
    for (Entry<String, Pattern> e : patterns.entrySet()) {
      if (e.getValue().matcher(string).matches()) {
        return Optional.of(values.get(e.getKey()));
      }
    }
    return Optional.absent();
  }

  /** Returns a read-only view of the underlying pattern:value mapping. */
  public Map<String, V> asPatternMap() {
    return Collections.unmodifiableMap(values);
  }
}

组合比继承有很多好处。除了不需要实现完整的Map 合约之外,我们还可以给我们的方法更清晰的名称和更好的签名。 .find() 清楚地表明我们正在进行可能昂贵的搜索,而 .get() 通常暗示要快。

您的示例最终将如下所示(您可能需要标准的 functional interface 作为 V 类型,但这取决于您的需要):

RegexLookup<...> configuration = new RegexLookup();
configuration.putPattern('^RESOURCE.*', () -> { /* some action*/});
configuration.putPattern('USER|ACCOUNT', () -> { /* do action*/}};
configuration.putPattern('PRODUCT', () -> { /* do action */}};

然后您可以使用以下命令检索操作:

Optional<...> action = configuration.find(someString);

这个实现有一些可能的改进可能让我们比O(nk)做得更好,例如构造模式的连接(|)和做本质上是二分搜索,但进行额外的正则表达式搜索的开销可能不值得(我认为复杂度变为 O(log(n) * k^2)),所以我肯定想要对比上述实现更复杂的东西进行基准测试。

【讨论】:

  • 最后用这种方法将 lambda 表达式用于 V
  • 很高兴听到它有帮助。
猜你喜欢
  • 2015-04-27
  • 1970-01-01
  • 1970-01-01
  • 2016-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-06
相关资源
最近更新 更多