【问题标题】:Parse a formatted string into a map which has same key many times?将格式化的字符串解析为多次具有相同键的映射?
【发布时间】:2017-09-21 20:11:17
【问题描述】:

我有一个以特定格式出现的字符串,所以我想将该字符串解析为映射。我有一个将 String 解析为 Map 的方法,它工作正常。

  public static Map<String, String> parseStringToMap(String payload) {
    try {
      return Maps.newHashMap(Splitter.on("|").withKeyValueSeparator("=").split(payload));
    } catch (Exception ex) {
      return ImmutableMap.of();
    }
  }

字符串示例:

"type=3|Id=23456|user=13456"

现在有时当我在同一个字符串payload 中有两次具有相同值的相同键时,我上面的方法会失败并抛出异常。例如对于下面的字符串,它不起作用并且失败,然后它返回空映射,因为它有两次 type 键。

"type=3|Id=23456|user=13456|type=3"

我能做些什么来解决这个问题而不是修复字符串有效负载?因此,假设相同的键多次出现,则覆盖映射中的该键值而不是失败。我想返回一个可变映射。

我仍在使用 Java 7。最好和最有效的方法是什么?

【问题讨论】:

  • 你能使用 Java 8 吗?如果是这样,您可以使用分组收集器,构建Map&lt;String, Set&lt;String&gt;&gt;,或类似的。
  • @AndyTurner 不幸的是,我仍在使用 Java 7,还不能使用 Java 8。

标签: java hashmap guava splitter


【解决方案1】:

文档说如果有重复的键,拆分会抛出异常:

@CheckReturnValue public Map split(CharSequence 序列)

将序列拆分为子字符串,将每个子字符串拆分为一个条目, 并返回包含每个条目的不可修改映射。例如, Splitter.on(';').trimResults().withKeyValueSeparator("=>") .split("a=>b ; c=>b") 将返回从 "a" 到 "b" 和 "c" 到 b.

返回的映射保留了序列中条目的顺序。

抛出: IllegalArgumentException - 如果指定的序列没有拆分成有效的映射条目,或者如果有重复的键

所以,您不能使用 MapSplitter 的拆分,抱歉!

使用 Java 8:

    return Splitter
        .on("|")
        .splitToList(payload)
        .stream()
        .map(kv -> kv.split("="))
        .collect(Collectors.toMap(kv -> kv[0], kv -> kv[1], (d1, d2) -> d1));

【讨论】:

  • 是的,除了拆分器,还有什么更好的方法吗?
  • @user1950349 也许您可以先使用正则表达式修复您的输入?
  • 不幸的是,我仍在使用 Java 7。
【解决方案2】:

我不知道如何在 Guava 中做到这一点,但使用普通的旧 Java 并不太难:

Map<String, String> map = new HashMap<>();
for (String part : payload.split("\\|")) {
  String[] subparts = part.split("=", 2);
  map.put(subparts[0], subparts[1]);
}
return map;

如果您只想检查字符串的格式是否正确(即有管道分隔的元素,每个元素都包含一个= 符号),那么您可以使用indexOf 来查找这些:

int start = 0;
while (start < payload.length()) {
  int end = payload.indexOf('|', start);
  if (end == -1) {
    end = payload.length();
  }

  int equalsPos = payload.indexOf('=', start);
  if (equalsPos > end || equalsPos < 0) {
    // No = found between adjacent |s. Not valid format.
    return Collections.emptyMap();
  }

  // Extract the substrings between | and =, and = and the next |.
  map.put(payload.substring(start, equalsPos), payload.substring(equalsPos + 1, end));

  start = end + 1;
}

【讨论】:

  • 通过这种方式如何检查它们是否仅采用该格式?如果它们不是那种格式,那么返回空地图?或者我应该把它包装在 try catch 中,如果它们不是那种格式并且基于返回的空映射,它会自行抛出异常?
  • “不是那种格式”是什么意思?有一个=,然后是|,然后是= 等等,中间有字符?当然:只需使用 indexof 搜索字符串中的那些。
  • 是的,它只能是这种格式,如果不是,则返回空地图。
  • 我在这个有效载荷上尝试了你编辑的建议,它不是有效格式 type=3:Id=23456|user=13456|type=3 并且它不返回空映射,因为它认为这是一个有效的字符串。
  • 在什么方面是无效的?它在所有管道(字符串的 /start 和 end)之间都有一个等号。第一个值中有多个等于并不会自动使其无效;这只是您的(未说明的)要求。但是,如果这就是您想要的,为什么不尝试修改此代码呢?这不是一个很难的改变。
猜你喜欢
  • 1970-01-01
  • 2010-11-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-07
  • 2017-10-31
  • 2018-02-02
相关资源
最近更新 更多