过滤值不区分大小写的字符串集合

filter collection of strings with values case insensitive

如何过滤我的集合中的值以检查是否相等,忽略大小写。

示例: 我有

["Value1", "vALue1", "vALue2", "valUE2"]

我需要

["Value1", "vALue2"]

任何解决方案都会很好。

例如,如果我已经有等于忽略大小写的字符串,我可以禁止添加新字符串

或者我可以收集并过滤它以去除等于忽略大小写的字符串

这是一个最简单的代码示例,说明它是如何工作的:

String[] array = new String[] {"Value1", "vALue1", "vALue2", "valUE2"};
ArrayList<String> finalArray = new ArrayList<>();
for(String entry : array) {
    boolean alreadyContained = finalArray.stream().anyMatch(entry::equalsIgnoreCase);
    if(!alreadyContained) {
        finalArray.add(entry);
    }
}

基本上,您创建一个包含所有非重复条目的 ArrayList。对于每个条目,检查它是否已包含在 ArrayList 中(忽略大小写),否则添加它。

单独使用 Streams1,这似乎无法(轻松)实现,但您可以跟踪 Set 中已经看到的元素(O(1 ) 查找) 和 filter 元素,根据它们的小写形式是否已经在该集合中(Set.add 将 return false 然后)。

List<String> values = List.of("Value1", "vALue1", "vALue2", "valUE2");
Set<String> seen = new HashSet<>();
List<String> res = values.stream().filter(s -> seen.add(s.toLowerCase()))
                                  .collect(Collectors.toList());
System.out.println(res);  // [Value1, vALue2]

1) 例如,distinct 不接受映射函数并且 Collectors.groupingBy 可能不保留顺序。

编辑

这是 tobias_k's 的通用示例:

import java.util.*;
import java.util.stream.Collectors;

public class ArrayUtils {
    public static void main(String[] args) {
        List<String> values = List.of("Value1", "vALue1", "vALue2", "valUE2");
        List<String> deduped = dedupeCaseInsensitive(values);

        System.out.println(deduped);  // [Value1, vALue2]
    }

    /* Higher-order function */
    public static List<String> dedupeCaseInsensitive(List<String> collection) {
        return dedupeWith(collection, String.CASE_INSENSITIVE_ORDER);
    }

    public static <E> List<E> dedupeWith(List<E> list, Comparator<E> comparator) {
        Set<E> seen = new TreeSet<>(comparator);
        return list.stream().filter(s -> seen.add(s)).collect(Collectors.toList());
    }
}

原始编辑

这是一个流版本:

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

public class ArrayUtils {
    public static void main(String[] args) {
        String[] items = {"Value1", "vALue1", "vALue2", "valUE2"};
        String[] result = dedupeCaseInsensitive(items);

        // Print the resulting array.
        System.out.println(Arrays.toString(result));
    }

    public static String[] dedupeCaseInsensitive(String[] items) {
        return Arrays.stream(items)
            .collect(Collectors.toMap(
                String::toLowerCase,
                Function.identity(),
                (o1, o2) -> o1,
                LinkedHashMap::new))
            .values()
            .stream()
            .toArray(String[]::new);
    }
}

原始回复

您可以通过填充 Map、获取其值并对其进行排序来使用不区分大小写的逻辑进行重复数据删除。

import java.util.*;

public class ArrayUtils {
    public static void main(String[] args) {
        String[] items = {"Value1", "vALue1", "vALue2", "valUE2"};
        String[] result = dedupeCaseInsensitive(items);

        // Print the resulting array.
        System.out.println(Arrays.toString(result));
    }

    public static String[] dedupeCaseInsensitive(String[] items) {
        Map<String, String> map = new HashMap<String, String>();

        // Filter the values using a map of key being the transformation,
        // and the value being the original value.
        for (String item : items) {
            map.putIfAbsent(item.toLowerCase(), item);
        }

        List<String> filtered = new ArrayList<>(map.values());

        // Sort the filtered values by the original positions.
        Collections.sort(filtered,
                Comparator.comparingInt(str -> findIndex(items, str)));

        return collectionToArray(filtered);
    }

    /* Convenience methods */

    public static String[] collectionToArray(Collection<String> collection) {
        return collection.toArray(new String[collection.size()]);
    }

    public static int findIndex(String arr[], String t) {
        return Arrays.binarySearch(arr, t);
    }
}

如果您使用 LinkedHashMap,则不需要排序,因为项目会保留它们的插入顺序。

import java.util.*;

public class ArrayUtils {
    public static void main(String[] args) {
        String[] items = {"Value1", "vALue1", "vALue2", "valUE2"};
        String[] result = dedupeCaseInsensitive(items);

        // Print the resulting array.
        System.out.println(Arrays.toString(result));
    }

    public static String[] dedupeCaseInsensitive(String[] items) {
        Map<String, String> map = new LinkedHashMap<String, String>();

        // Filter the values using a map of key being the transformation,
        // and the value being the original value.
        for (String item : items) {
            map.putIfAbsent(item.toLowerCase(), item);
        }

        return collectionToArray(map.values());
    }

    /* Convenience methods */

    public static String[] collectionToArray(Collection<String> collection) {
        return collection.toArray(new String[collection.size()]);
    }
}}

一些 Java 提供 distinctBy 功能的库可用于解决此任务。

例如,StreamEx库(GitHub, Maven Repo),代表流的扩展API,可以这样使用:

import java.util.*;
import one.util.streamex.*;

public class MyClass {
    public static void main(String args[]) {
        String[] data = {
            "Value1", "vALue1", "vALue2", "valUE2"
        };
        List<String> noDups = StreamEx.of(data)
                .distinct(String::toLowerCase)
                .toList();
        System.out.println(noDups);
    }
}

输出:

[Value1, vALue2]