Java中的正则表达式映射实现

时间:2017-06-07 15:23:25

标签: java regex dictionary data-structures

是否有任何Map的Java实现,哪些键是Regex模式。这种数据结构有可能吗? 找到了几个手工制作的解决方案,如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);

它将用作静态配置。因此删除功能并不重要。

EDITED

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

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

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

  • 使用if / else
  • 更改开关
  • 建议使用dimo414之类的一些RegEx容器。
  • 创建一些配置类。这看起来太复杂了。

我在groovy中解决了这个问题非常容易,

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

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

1 个答案:

答案 0 :(得分:1)

Map有一个相当复杂的合同,很难(或不可能)正确地遵循您描述的数据结构。例如,实现.entrySet()没有任何有意义的方法,因为有无限的有效密钥。此外,这个&#34; map&#34;的行为并不真正符合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)更好,例如构建联合({{1}模式和基本上进行二进制搜索,但进行额外的正则表达式搜索的开销可能不值得(复杂性变为 O(log(n)* k ^ 2),我认为,所以我绝对想要比上述实现更复杂的基准测试。