Java 字符数组映射
Java Char Array To Map
正在寻找一种方法将 String 转换为 "char" => int(索引)的映射。显然,这假设 String 具有唯一字符。
有没有办法用 Streams 做到这一点?我认为这会起作用,但我 运行 在尝试编译它时遇到错误:
String alphabet = "0123456789";
alphabet.chars().collect(Collectors.toMap(alphabet::charAt, Function.identity()));
基本上我想要的东西等同于 Python 代码:
{c, i for i,c in enumerate("0123456789")}
这有效。
Map<Integer, Character> map =
IntStream.range(0, alphabet.length()).boxed()
.collect(Collectors.toMap(x -> x, alphabet::charAt));
编辑:
当然,如果您改用 Scala(也在 JVM 上运行),您可以将它写得更短。然后就是 alphabet.zipWithIndex.toMap
这将创建一个从每个字符到其整数的映射:
Map<Character, Integer> map = IntStream.range(0, alphabet.length())
.boxed().collect(Collectors.toMap(alphabet::charAt, Function.identity()));
请注意,如果您有重复的字符,它将不起作用。
您在评论中询问了如何使用 3 个参数 IntStream#collect
。这是:
Map<Character, Integer> map = IntStream.range(0, alphabet.length())
.collect(HashMap::new, (m, i) -> m.put(alphabet.charAt(i), i), Map::putAll);
如果你想像 toMap
那样进行重复检查并且仍然使用 3 参数形式,你将必须更新 both 第二个(累加器)和第三个(组合器)参数进行检查:
map = IntStream.range(0, alphabet.length())
.collect(
HashMap::new,
(m, i) -> m.merge(alphabet.charAt(i), i,
(a,b) -> {throw new IllegalStateException("duplicate!");}
),
(m1, m2) -> m2.forEach((c,i) ->
m1.merge(c, i, (a,b) -> {throw new IllegalStateException("duplicate!");})
)
);
如您所见,这变得很难看,您可能希望定义一个辅助方法来简化它。如果您愿意禁止并行流,您可以只检查累加器中的重复项,但随后您将不得不让组合器抛出异常:
map = IntStream.range(0, alphabet.length())
.collect(
HashMap::new,
(m, i) -> m.merge(alphabet.charAt(i), i,
(a,b) -> {throw new IllegalStateException("duplicate!");}
),
(m1, m2) -> {throw new AssertionError("parallel not allowed");}
);
除非您将此作为练习,否则最好将所有这些混乱留给 toMap
处理。这就是它的用途。
正如您从此处发布的各种解决方案中看到的那样,stream()
并没有让事情变得容易很多。
虽然函数式风格的意大利面条代码现在风靡一时,但我建议使用命令式编程以提高可读性。
for(int i=0; i<s.length(); i++) map.put(s.charAt(i), i);
说真的,程序有时会更好!人们低估了程序代码的清晰度和效率。
但是,您可能希望在您的场景中使用此代码:
if (c<'0' || c>'9') throw RuntimeException("Not a digit");
int digit = c - '0';
比 Map
.
便宜 很多 (更快,0 内存)
正在寻找一种方法将 String 转换为 "char" => int(索引)的映射。显然,这假设 String 具有唯一字符。
有没有办法用 Streams 做到这一点?我认为这会起作用,但我 运行 在尝试编译它时遇到错误:
String alphabet = "0123456789";
alphabet.chars().collect(Collectors.toMap(alphabet::charAt, Function.identity()));
基本上我想要的东西等同于 Python 代码:
{c, i for i,c in enumerate("0123456789")}
这有效。
Map<Integer, Character> map =
IntStream.range(0, alphabet.length()).boxed()
.collect(Collectors.toMap(x -> x, alphabet::charAt));
编辑:
当然,如果您改用 Scala(也在 JVM 上运行),您可以将它写得更短。然后就是 alphabet.zipWithIndex.toMap
这将创建一个从每个字符到其整数的映射:
Map<Character, Integer> map = IntStream.range(0, alphabet.length())
.boxed().collect(Collectors.toMap(alphabet::charAt, Function.identity()));
请注意,如果您有重复的字符,它将不起作用。
您在评论中询问了如何使用 3 个参数 IntStream#collect
。这是:
Map<Character, Integer> map = IntStream.range(0, alphabet.length())
.collect(HashMap::new, (m, i) -> m.put(alphabet.charAt(i), i), Map::putAll);
如果你想像 toMap
那样进行重复检查并且仍然使用 3 参数形式,你将必须更新 both 第二个(累加器)和第三个(组合器)参数进行检查:
map = IntStream.range(0, alphabet.length())
.collect(
HashMap::new,
(m, i) -> m.merge(alphabet.charAt(i), i,
(a,b) -> {throw new IllegalStateException("duplicate!");}
),
(m1, m2) -> m2.forEach((c,i) ->
m1.merge(c, i, (a,b) -> {throw new IllegalStateException("duplicate!");})
)
);
如您所见,这变得很难看,您可能希望定义一个辅助方法来简化它。如果您愿意禁止并行流,您可以只检查累加器中的重复项,但随后您将不得不让组合器抛出异常:
map = IntStream.range(0, alphabet.length())
.collect(
HashMap::new,
(m, i) -> m.merge(alphabet.charAt(i), i,
(a,b) -> {throw new IllegalStateException("duplicate!");}
),
(m1, m2) -> {throw new AssertionError("parallel not allowed");}
);
除非您将此作为练习,否则最好将所有这些混乱留给 toMap
处理。这就是它的用途。
正如您从此处发布的各种解决方案中看到的那样,stream()
并没有让事情变得容易很多。
虽然函数式风格的意大利面条代码现在风靡一时,但我建议使用命令式编程以提高可读性。
for(int i=0; i<s.length(); i++) map.put(s.charAt(i), i);
说真的,程序有时会更好!人们低估了程序代码的清晰度和效率。
但是,您可能希望在您的场景中使用此代码:
if (c<'0' || c>'9') throw RuntimeException("Not a digit");
int digit = c - '0';
比 Map
.