Java 8从列表中删除重复的字符串,而不考虑大小写



我们如何在不考虑每个单词大小写的情况下从字符串列表中删除重复元素,例如考虑下面的代码片段

String str = "Kobe Is is The the best player In in Basketball basketball game .";
List<String> list = Arrays.asList(str.split("\s"));
list.stream().distinct().forEach(s -> System.out.print(s+" "));

这仍然给出了与下面相同的输出,这是显而易见的

Kobe Is is The the best player In in Basketball basketball game .

我需要如下的结果

Kobe Is The best player In Basketball game .

从字面上理解你的问题,要"从列表中删除重复的字符串,无论大小写",你可以使用

// just for constructing a sample list
String str = "Kobe Is is The the best player In in Basketball basketball game .";
List<String> list = new ArrayList<>(Arrays.asList(str.split("\s")));
// the actual operation
TreeSet<String> seen = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
list.removeIf(s -> !seen.add(s));
// just for debugging
System.out.println(String.join(" ", list));

如果只需要消除连续的重复项,可以使用正则表达式。下面的正则表达式检查重复单词,忽略大小写。

String input = "Kobe Is is The the best player In in Basketball basketball game .";
String output = input.replaceAll("(?i)\b(\w+)\s+\1\b", "$1");
System.out.println(output);

哪个输出:

Kobe Is The best player In Basketball game .

这里有一个有趣的解决方案,可以通过使用流来获得预期的结果。

String result = Pattern.compile("\s")
.splitAsStream(str)
.collect(Collectors.collectingAndThen(Collectors.toMap(String::toLowerCase,
Function.identity(),
(l, r) -> l,
LinkedHashMap::new),
m -> String.join(" ", m.values())));

打印:

Kobe Is The best player In Basketball game .

如果打印所有大写字母时丢失对您来说不是问题,您可以用这种方式

list.stream()
.map(String::toLowerCase)
.distinct()
.forEach(System.out::print)

输出:

科比是篮球比赛中最好的球员。

保留大写字母并删除小写字母:

String str = "Kobe Is is The the best player In in Basketball basketball game .";
List<String> list = Arrays.asList(str.split("\s"));
for(int i = 1; i<list.size(); i++)
{
if(list.get(i).equalsIgnoreCase(list.get(i-1)))
{
// is lower case
if(list.get(i).toLowerCase().equals(list.get(i)))
{
list.set(i,"");
}
else
{
list.set(i-1, "");
}
}
}
list.stream().distinct().forEach(s -> System.out.print(s+" "));             

重复字符串的问题是,它们不是以完全相同的大小写出现的。第一个单词是Basketball,另一个是basketball,所以这两个单词不是同一个。大写字母B第一次出现就在那里。所以你可以做的是,你可以把字符串比较成小写或大写,或者你可以忽略大小写进行比较。

这里有一个单行解决方案:

  • 过滤掉所有(包括非连续的(不区分大小写的重复项(与Robby的解决方案和maio290的解决方案不同(
  • 保留原始情况(与Leviand的解决方案不同(

此解决方案利用了jOOλ库及其Seq.distinct(Function<T,U>)方法:

List<String> distinctWords = Seq.seq(list).distinct(String::toLowerCase).toList();

结果(如问题中所示打印时(:

科比是篮球比赛中最好的球员。

TreeSet提供的解决方案非常优雅。但是TreeSet也对元素进行排序,这使得解决方案效率低下。下面的代码演示了如何使用HashMap更有效地实现它,该HashMap优先于具有更多大写字母的字符串

class SetWithIgnoreCase {
private HashMap<String, String> underlyingMap = new HashMap<>();
public void put(String str) {
String lowerCaseStr = str.toLowerCase();
underlyingMap.compute(lowerCaseStr, (k, v) -> (v == null) ? str : (compare(v, str) > 0 ? v : str));
}
private int compare(String str1, String str2) {
int upperCaseCnt1 = 0;
int upperCaseCnt2 = 0;
for (int i = 0; i < str1.length(); i++) {
upperCaseCnt1 += (Character.isUpperCase(str1.charAt(i)) ? 1 : 0);
upperCaseCnt2 += (Character.isUpperCase(str2.charAt(i)) ? 1 : 0);
}
return upperCaseCnt1 - upperCaseCnt2;
}
}

最新更新