【问题标题】:Using Lambda expression to replace chars in string using HashMap使用 Lambda 表达式使用 HashMap 替换字符串中的字符
【发布时间】:2016-11-09 20:09:45
【问题描述】:

所以我正在做一个项目,我必须将包含希腊字母的字符串转换为包含该字母的英文表示的字符串。所以希腊语的α 会变成alphaΑ 会变成Alpha

我创建了一个 HashMap,它具有从 Unicode 字符到普通字符串的适当转换。

我使用了一个简单的For 循环来检查输入String 中的Char 是否是HashMap 中的Key,如果是,那么我将使用@987654330 附加它的替换@。这是我用来执行此操作的代码:

char[] ca = snippet.toCharArray();
        StringBuilder out = new StringBuilder();
        for (char c : ca) {
            if (GREEK_LETTER_DICT.containsKey(Character.toString(c))) {
                out.append( GREEK_LETTER_DICT.get(Character.toString(c)));
            } else {
                out.append(c);
            }
        }
    return out.toString();

这是一个以String 作为输入的公共静态方法。 我想知道的是是否可以用 lambda 表达式做同样的事情?我已经找到了几个解决方案来替换String 中的Character,但它们不使用HashMap/Dictionary

我知道仅仅为了使用 lambda 表达式而将其转换为 lambda 表达式是完全没用的,但是因为我还有另外 7 个这样的函数将我的代码缩短了近 60%,我想看看它是否完全有可能在“一行”代码中做到这一点。这样我可以减少我使用的单独方法的数量并获得更清晰的代码。

到目前为止,我已经找到了一种将String 转换为Stream 的方法:

String.chars()

然后将此IntStream 转换为Stream<Character>

.mapToObj(ch -> (char) ch)

并过滤以查看Character 是否在HashMap

.filter(ch -> (GREEK_LETTER_DICT.containsKey(ch)))

问题在于我做不到

  1. 将原始Character 附加到输出String 如果它不在HashMap
  2. Character 替换为HashMap 中的字符串

因此,感谢您对这两点的任何帮助。我发现有时我想错了,因为不是查看String 中的Character 是否等于选项列表中的Character,我应该检查该选项列表是否返回正值index。所以这个(伪代码):

"StringWithOptions".indexOf('characterLiteral')

而不是这个:

Character.equals("One|of|these")

【问题讨论】:

  • 基于有问题的代码str.chars().forEach(character -> out.append(GREEK_LETTER_DICT.getOrDefault(Character.toString(c), c)));
  • 那很快 ;-) 感谢您的回复。使用此代码能够使其正常工作。我会把它放在这里。我不得不对代码进行一些更改。
  • @SMA 美丽。我建议您将其放入答案中。

标签: java lambda


【解决方案1】:

怎么样:

public static String replaceGreekLetters(String snippet) {
    return snippet
            .chars()
            .mapToObj(c -> (char) c)
            .map(c -> GREEK_LETTER_DICT.getOrDefault(c, c.toString()))
            .collect(Collectors.joining());
}

【讨论】:

  • 这比操作状态变量(Stringbuilder)更好,它提供了更好的并行性:)
  • @niceman 这个过滤器可以并行执行。那么你会建议不要使用StringBuilder 吗?
  • @NathanvanDalen 是的,我愿意
  • 如果不将c.toString() 也添加到getOrDefault 的第一个参数中,我将无法使其工作。我认为mapToObj 应该映射到String
  • @4Castle 我的初始 TS 包含一个错误,其中 GREEK_LETTER_DICT.containsKey(Character.toString(c)) 应该是 GREEK_LETTER_DICT.containsKey(c),因为 HashMap 是 但我忘了更改它。 @xehpuk 的代码是正确的。
【解决方案2】:

我会这样做:

str
    .chars()
    .forEach(
        character -> out.append(
            GREEK_LETTER_DICT.getOrDefault(Character.toString(c), c)
        )
    );

【讨论】:

  • map 不是更好吗?那么 OP 就根本不需要 StringBuilder 了?
  • 这只是使用流的借口。如果它是有状态的,则为每个循环使用一个法线。
【解决方案3】:

使用 Steam 有几个很好的解决方案。我更喜欢简单的:

  • 避免从 char 转换为 String
  • 避免在 Stream 管道外使用 StringBuffer
  • 使用预定义的收集器
public class ReplaceGreek {

    private Map<String, String> DICT = new HashMap<>();
    {
        DICT.put("α", "alpha");
    }

    public String replace(final String original) {
        return Arrays.stream(original.split(""))
                .map(c -> DICT.getOrDefault(c, c))
                .collect(Collectors.joining());
    }
}

它甚至可以工作!

public class ReplaceGreekTest {

    @Test
    public void test() {
        ReplaceGreek rg = new ReplaceGreek();
        String greek = "This is greek: α";

        String nogreek = rg.replace(greek);

        assertEquals("This is greek: alpha", nogreek);
    }

}

【讨论】:

  • OP 正在使用Map&lt;Character, String&gt;。不过很好的解决方案!
  • 但是这个线程安全吗?我想这是因为String 本身就是。这确实是一个非常优雅的解决方案,只需 3 行代码。与我之前的 10 行相比,可读性有很大差异。 @4castle:不过我可以轻松切换回&lt;String, String&gt;
  • @NathanvanDalen 是的,它是线程安全的。它的作用与其他解决方案非常相似。
  • 您可以使用Pattern.compile("").splitAsStream(original) 而不是Arrays.stream(original.split("")) 来避免创建临时数组。不过,original.chars().mapToObj(c -&gt; Character.toString((char)c)) 效率更高。
【解决方案4】:

所以使用SMAMartin Seeler 中的代码,我能够让它工作。生成的代码现在返回带有正确结果的String,并通过了我的所有单元测试。

这是生成的代码:

    return snippet.chars()
            .mapToObj(c -> (char) c)
            .collect(Collector.of(StringBuilder::new,
                    (stringBuilder, c) -> stringBuilder.append(GREEK_LETTER_DICT.getOrDefault(c,c.toString())),
                    StringBuilder::append,
                    StringBuilder::toString));

SMA 的代码存在一个问题,即HashMap 使用&lt;Character, String&gt; 而不是&lt;String, String&gt;。所以我不得不改变#getOrDefault的参数。将它与.mapToObj(c -&gt; (char) c) 结合使用就可以了。

现在让我们看看我是否可以改变这个:

(stringBuilder, c) -> stringBuilder.append(GREEK_LETTER_DICT.getOrDefault(c,c.toString())),
                        StringBuilder::append

类似于StringBuilder::append(GREEK_LETTER_DICT.getOrDefault(c,Character.toString(c))

【讨论】:

  • ……你认为这比你原来的循环更短/更简单?
猜你喜欢
  • 1970-01-01
  • 2011-04-12
  • 1970-01-01
  • 1970-01-01
  • 2015-02-27
  • 1970-01-01
  • 2011-10-05
相关资源
最近更新 更多