Java 编程问题:一、字符串、数字和数学2

简介: Java 编程问题:一、字符串、数字和数学2

5 元音和辅音计数


以下代码适用于英语,但取决于您所涵盖的语言种类,元音和辅音的数量可能会有所不同,因此应相应调整代码。


此问题的第一个解决方案需要遍历字符串并执行以下操作:


   我们需要检查当前字符是否是元音(这很方便,因为英语中只有五个纯元音;其他语言有更多元音,但数量仍然很小)。


   如果当前字符不是元音,则检查它是否位于'a'和'z'之间(这意味着当前字符是辅音)。


注意,最初,给定的String对象被转换为小写。这有助于避免与大写字符进行比较。例如,仅对'a'进行比较,而不是对'A'和'a'进行比较。


此解决方案的代码如下:

private static final Set<Character> allVowels
            = new HashSet(Arrays.asList('a', 'e', 'i', 'o', 'u'));
public static Pair<Integer, Integer> 
    countVowelsAndConsonants(String str) {
  str = str.toLowerCase();
  int vowels = 0;
  int consonants = 0;
  for (int i = 0; i < str.length(); i++) {
    char ch = str.charAt(i);
    if (allVowels.contains(ch)) {
      vowels++;
    } else if ((ch >= 'a' && ch <= 'z')) {
      consonants++;
    }
  }
  return Pair.of(vowels, consonants);
}



在 Java8 函数式中,可以使用chars()filter()重写此代码:

private static final Set<Character> allVowels
            = new HashSet(Arrays.asList('a', 'e', 'i', 'o', 'u'));
public static Pair<Long, Long> countVowelsAndConsonants(String str) {
  str = str.toLowerCase();
  long vowels = str.chars()
    .filter(c -> allVowels.contains((char) c))
    .count();
  long consonants = str.chars()
    .filter(c -> !allVowels.contains((char) c))
    .filter(ch -> (ch >= 'a' && ch<= 'z'))
    .count();
  return Pair.of(vowels, consonants);
}


相应地过滤给定的字符串,count()终端操作返回结果。依赖partitioningBy()将减少代码,如下所示:

Map<Boolean, Long> result = str.chars()
  .mapToObj(c -> (char) c)
  .filter(ch -> (ch >= 'a' && ch <= 'z'))
  .collect(partitioningBy(c -> allVowels.contains(c), counting()));
return Pair.of(result.get(true), result.get(false));

完成!现在,让我们看看如何计算字符串中某个字符的出现次数。




6 计算某个字符的出现次数


此问题的简单解决方案包括以下两个步骤:

  1. 将给定字符串中出现的每个字符替换为""(基本上,这类似于删除给定字符串中出现的所有字符)。
  2. 从初始字符串的长度中减去在第一步中获得的字符串的长度。



此方法的代码如下:

public static int countOccurrencesOfACertainCharacter(
    String str, char ch) {
  return str.length() - str.replace(String.valueOf(ch), "").length();
}


以下解决方案还包括 Unicode 代理项对:

public static int countOccurrencesOfACertainCharacter(
    String str, String ch) { 
  if (ch.codePointCount(0, ch.length()) > 1) {
    // there is more than 1 Unicode character in the given String
    return -1; 
  }
  int result = str.length() - str.replace(ch, "").length();
  // if ch.length() return 2 then this is a Unicode surrogate pair
  return ch.length() == 2 ? result / 2 : result;
}



另一个易于实现且快速的解决方案包括循环字符串(一次遍历)并将每个字符与给定的字符进行比较。每一场比赛增加一个计数器:

public static int countOccurrencesOfACertainCharacter(
    String str, char ch) {
  int count = 0;
  for (int i = 0; i < str.length(); i++) {
    if (str.charAt(i) == ch) {
      count++;
    }
  }
  return count;
}



涵盖 Unicode 代理项对的解决方案包含在本书附带的代码中。在 Java8 函数风格中,一种解决方案是使用filter()reduce()。例如,使用filter()将产生以下代码:

public static long countOccurrencesOfACertainCharacter(
    String str, char ch) {
  return str.chars()
    .filter(c -> c == ch)
    .count();
}


涵盖 Unicode 代理项对的解决方案包含在本书附带的代码中。


对于第三方库支持,请考虑 Apache Commons Lang、StringUtils.countMatches()、Spring Framework、StringUtils.countOccurrencesOf()和 Guava、CharMatcher.is().countIn()。



7 将字符串转换为intlongfloatdouble

让我们考虑以下字符串(也可以使用负数):

private static final String TO_INT = "453"; 
private static final String TO_LONG = "45234223233"; 
private static final String TO_FLOAT = "45.823F";
private static final String TO_DOUBLE = "13.83423D";

String转换成IntegerLongFloatDouble对象,可以通过以下 Java 方法来完成:Integer.valueOf()Long.valueOf()Float.valueOf()Double.valueOf()

Integer toInt = Integer.valueOf(TO_INT);
Long toLong = Long.valueOf(TO_LONG);
Float toFloat = Float.valueOf(TO_FLOAT);
Double toDouble = Double.valueOf(TO_DOUBLE);


String无法成功转换时,Java 抛出NumberFormatException异常。以下代码不言自明:

private static final String WRONG_NUMBER = "452w";
try {
  Integer toIntWrong1 = Integer.valueOf(WRONG_NUMBER);
} catch (NumberFormatException e) {
  System.err.println(e);
  // handle exception
}
try {
  int toIntWrong2 = Integer.parseInt(WRONG_NUMBER);
} catch (NumberFormatException e) {
  System.err.println(e);
  // handle exception
}

对于第三方库支持,请考虑 ApacheCommons BeanUtils:IntegerConverterLongConverterFloatConverterDoubleConverter




8 从字符串中删除空格


这个问题的解决方案是使用带有正则表达式的String.replaceAll()方法。主要是\s删除所有的空白,包括不可见的空白,如\t\n\r

public static String removeWhitespaces(String str) {
  return str.replaceAll("\\s", "");
}

从 JDK11 开始,String.isBlank()检查字符串是空的还是只包含空格代码点。对于第三方库支持,请考虑 Apache Commons Lang,StringUtils.deleteWhitespace()和 Spring 框架,StringUtils.trimAllWhitespace()。



9 用分隔符连接多个字符串


有几种解决方案很适合解决这个问题。在 Java8 之前,一种方便的方法依赖于StringBuilder,如下所示:

public static String joinByDelimiter(char delimiter, String...args) {
  StringBuilder result = new StringBuilder();
  int i = 0;
  for (i = 0; i < args.length - 1; i++) {
    result.append(args[i]).append(delimiter);
  }
  result.append(args[i]);
  return result.toString();
}

从 Java8 开始,这个问题至少还有三种解决方案。其中一个解决方案依赖于StringJoiner工具类。此类可用于构造由分隔符(例如逗号)分隔的字符序列。


它还支持可选的前缀和后缀(此处忽略):

public static String joinByDelimiter(char delimiter, String...args) {
  StringJoiner joiner = new StringJoiner(String.valueOf(delimiter));
  for (String arg: args) {
    joiner.add(arg);
  }
  return joiner.toString();
}


另一种解决方案依赖于String.join()方法。此方法是在 Java8 中引入的,有两种风格:

String join​(CharSequence delimiter, CharSequence... elems)
String join​(CharSequence delimiter,
  Iterable<? extends CharSequence> elems)


连接由空格分隔的多个字符串的示例如下:

String result = String.join(" ", "how", "are", "you"); // how are you


更进一步说,Java8 流和Collectors.joining()也很有用:

public static String joinByDelimiter(char delimiter, String...args) {
  return Arrays.stream(args, 0, args.length)
    .collect(Collectors.joining(String.valueOf(delimiter)));
}


注意通过+=运算符以及concat()和String.format()方法连接字符串。这些字符串可以用于连接多个字符串,但它们容易导致性能下降。例如,下面的代码依赖于+=并且比依赖于StringBuilder慢得多:


String str = "";``for(int i = 0; i < 1_000_000; i++) {`` str += "x";


+=被附加到一个字符串并重建一个新的字符串,这需要时间。


对于第三方库支持,请考虑 Apache Commons Lang,StringUtils.join()和 Guava,Joiner。




10 生成所有置换


涉及置换的问题通常也涉及递归性。基本上,递归性被定义为一个过程,其中给定了一些初始状态,并且根据前一个状态定义了每个连续状态


在我们的例子中,状态可以通过给定字符串的字母来具体化。初始状态包含初始字符串,每个连续状态可通过以下公式计算字符串的每个字母将成为字符串的第一个字母(交换位置),然后使用递归调用排列所有剩余字母。虽然存在非递归或其他递归解决方案,但这是该问题的经典解决方案。


将这个解决方案表示为字符串ABC,可以这样做(注意排列是如何完成的):


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-10RzDdgx-1657077108922)(https://github.com/apachecn/apachecn-java-zh/raw/master/docs/java-coding-prob/img/64849bb0-4ed5-4534-abfc-dff0fbe86514.png)]


对该算法进行编码将产生如下结果:


public static void permuteAndPrint(String str) {
  permuteAndPrint("", str);
}
private static void permuteAndPrint(String prefix, String str) {
  int n = str.length();
  if (n == 0) {
    System.out.print(prefix + " ");
  } else {
    for (int i = 0; i < n; i++) {
      permuteAndPrint(prefix + str.charAt(i),
        str.substring(i + 1, n) + str.substring(0, i));
    }
  }
}


最初,前缀应该是一个空字符串""。在每次迭代中,前缀将连接(固定)字符串中的下一个字母。剩下的字母将再次通过该方法传递。


假设这个方法存在于一个名为Strings的实用类中。你可以这样称呼它:

Strings.permuteAndStore("ABC");


这将产生以下输出:

ABC ACB BCA BAC CAB CBA


注意,这个解决方案在屏幕上打印结果。存储结果意味着将Set添加到实现中。最好使用Set,因为它消除了重复:

public static Set<String> permuteAndStore(String str) {
  return permuteAndStore("", str);
}
private static Set<String> 
    permuteAndStore(String prefix, String str) {
  Set<String> permutations = new HashSet<>();
  int n = str.length();
  if (n == 0) {
    permutations.add(prefix);
  } else {
    for (int i = 0; i < n; i++) {
      permutations.addAll(permuteAndStore(prefix + str.charAt(i),
        str.substring(i + 1, n) + str.substring(0, i)));
    }
  }
  return permutations;
}


例如,如果传递的字符串是TEST,那么Set将导致以下输出(这些都是唯一的排列):

ETST SETT TEST TTSE STTE STET TETS TSTE TSET TTES ESTT ETTS


使用List代替Set将产生以下输出(注意重复项):

TEST TETS TSTE TSET TTES TTSE ESTT ESTT ETTS ETST ETST ETTS STTE STET STET STTE SETT SETT TTES TTSE TEST TETS TSTE TSET


有 24 个排列。通过计算n阶乘(n!)。对于n = 4(字符串长度),4! = 1 x 2 x 3 x 4 = 24。当以递归方式表示时,这是n! = n x (n-1)!。


自从n!以极快的速度生成大量数据(例如,10! = 3628800),建议避免存储结果。对于 10 个字符的字符串(如直升机),有 3628800 个排列!


尝试用 Java8 函数式实现此解决方案将导致如下结果:


private static void permuteAndPrintStream(String prefix, String str) {
  int n = str.length();
  if (n == 0) {
    System.out.print(prefix + " ");
  } else {
    IntStream.range(0, n)
      .parallel()
      .forEach(i -> permuteAndPrintStream(prefix + str.charAt(i),
        str.substring(i + 1, n) + str.substring(0, i)));
  }
}


作为奖励,本书附带的代码中提供了返回Stream<String>的解决方案。

相关文章
|
8天前
|
算法 Java
【编程基础知识】Java打印九九乘法表
本文介绍了在Java中实现九九乘法表的三种方法:嵌套循环、数组和流控制。通过代码示例、流程图和表格对比,帮助读者深入理解每种方法的优缺点,提升编程技能。
30 2
|
8天前
|
存储 Java
【编程基础知识】 分析学生成绩:用Java二维数组存储与输出
本文介绍如何使用Java二维数组存储和处理多个学生的各科成绩,包括成绩的输入、存储及格式化输出,适合初学者实践Java基础知识。
37 1
|
4天前
|
安全 Java UED
Java中的多线程编程:从基础到实践
本文深入探讨了Java中的多线程编程,包括线程的创建、生命周期管理以及同步机制。通过实例展示了如何使用Thread类和Runnable接口来创建线程,讨论了线程安全问题及解决策略,如使用synchronized关键字和ReentrantLock类。文章还涵盖了线程间通信的方式,包括wait()、notify()和notifyAll()方法,以及如何避免死锁。此外,还介绍了高级并发工具如CountDownLatch和CyclicBarrier的使用方法。通过综合运用这些技术,可以有效提高多线程程序的性能和可靠性。
|
4天前
|
缓存 Java UED
Java中的多线程编程:从基础到实践
【10月更文挑战第13天】 Java作为一门跨平台的编程语言,其强大的多线程能力一直是其核心优势之一。本文将从最基础的概念讲起,逐步深入探讨Java多线程的实现方式及其应用场景,通过实例讲解帮助读者更好地理解和应用这一技术。
22 3
|
4天前
|
Java 开发者
在Java编程中,正确的命名规范不仅能提升代码的可读性和可维护性,还能有效避免命名冲突。
【10月更文挑战第13天】在Java编程中,正确的命名规范不仅能提升代码的可读性和可维护性,还能有效避免命名冲突。本文将带你深入了解Java命名规则,包括标识符的基本规则、变量和方法的命名方式、常量的命名习惯以及如何避免关键字冲突,通过实例解析,助你写出更规范、优雅的代码。
26 3
|
4天前
|
Java 程序员
在Java编程中,关键字不仅是简单的词汇,更是赋予代码强大功能的“魔法咒语”。
【10月更文挑战第13天】在Java编程中,关键字不仅是简单的词汇,更是赋予代码强大功能的“魔法咒语”。本文介绍了Java关键字的基本概念及其重要性,并通过定义类和对象、控制流程、访问修饰符等示例,展示了关键字的实际应用。掌握这些关键字,是成为优秀Java程序员的基础。
12 3
|
4天前
|
Java 程序员 编译器
在Java编程中,保留字(如class、int、for等)是具有特定语法意义的预定义词汇,被语言本身占用,不能用作变量名、方法名或类名。
在Java编程中,保留字(如class、int、for等)是具有特定语法意义的预定义词汇,被语言本身占用,不能用作变量名、方法名或类名。本文通过示例详细解析了保留字的定义、作用及与自定义标识符的区别,帮助开发者避免因误用保留字而导致的编译错误,确保代码的正确性和可读性。
16 3
|
4天前
|
算法 Java
在Java编程中,关键字和保留字是基础且重要的组成部分,正确理解和使用它们
【10月更文挑战第13天】在Java编程中,关键字和保留字是基础且重要的组成部分。正确理解和使用它们,如class、int、for、while等,不仅能够避免语法错误,还能提升代码的可读性和执行效率。本指南将通过解答常见问题,帮助你掌握Java关键字的正确使用方法,以及如何避免误用保留字,使你的代码更加高效流畅。
20 3
|
3天前
|
存储 安全 Java
了解final关键字在Java并发编程领域的作用吗?
在Java并发编程中,`final`关键字不仅用于修饰变量、方法和类,还在多线程环境中确保对象状态的可见性和不变性。本文深入探讨了`final`关键字的作用,特别是其在final域重排序规则中的应用,以及如何防止对象的“部分创建”问题,确保线程安全。通过具体示例,文章详细解析了final域的写入和读取操作的重排序规则,以及这些规则在不同处理器上的实现差异。
了解final关键字在Java并发编程领域的作用吗?
|
6天前
|
设计模式 SQL 安全
【编程进阶知识】Java单例模式深度解析:饿汉式与懒汉式实现技巧
本文深入解析了Java单例模式中的饿汉式和懒汉式实现方法,包括它们的特点、实现代码和适用场景。通过静态常量、枚举类、静态代码块等方式实现饿汉式,通过非线程安全、同步方法、同步代码块、双重检查锁定和静态内部类等方式实现懒汉式。文章还对比了各种实现方式的优缺点,帮助读者在实际项目中做出更好的设计决策。
22 0