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

9

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

    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 .

1
使用适当的比较器(https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#CASE_INSENSITIVE_ORDER)使用TreeSet。循环遍历每个单词,将其添加到集合中,如果已经存在,则不要打印它。 - JB Nizet
1
您是只查找连续的重复项,还是所有重复项? - Robby Cornelissen
8个回答

13

如果按字面意思解释您的问题,即“从列表中删除不考虑大小写的重复字符串”,您可以使用以下方法:

// 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));

3

如果您只想去掉连续重复的内容,可以使用正则表达式。下面的正则表达式检查重复单词,忽略大小写。

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 .

当使用java.util.regex.Pattern时,可以省略(?i)并编写Pattern.compile("\\b(\\w+)\\s+\\1\\b", Pattern.CASE_INSENSITIVE) - Lino
@Lino 是的,但是 String.replaceAll() 不接受 Pattern 参数。 - Robby Cornelissen
1
myPattern.matcher(input).replaceAll("$1")应该可以正常工作。 - Lino

3

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

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 .

1

如果你在输出所有大写字母时不介意出现问题,可以按照以下方式操作

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

输出:

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


1
似乎 OP 不想要全部小写。 - soorapadman

0

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

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+" "));             

0

1
通过以下方式也可以实现按属性去重:https://dev59.com/v2Ag5IYBdhLWcg3wWpxE - Eugene
不错的评论。顺便说一下,这几乎就是在Seq中实现的方式,唯一的区别是那里没有使用ConcurrentHashMap.newKeySet()(而是使用了常规的ConcurrentHashMap)。 - Tomasz Linkowski

0
重复字符串的问题在于它们不以相同的大小写形式出现,第一个单词是“Basketball”,而另一个单词是“basketball”,因此两者并不相同。第一次出现时有大写字母B。所以你可以将字符串比较为小写或大写形式,或者忽略大小写进行比较。

0
提供的使用TreeSet的解决方案十分优雅。但是,由于TreeSet会对元素进行排序,这使得该解决方案效率较低。 下面的代码演示了如何更高效地使用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;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接