繁体   English   中英

从字符串数组列表中删除重复项

[英]Remove duplicates from a list of String Array

我知道有很多关于“删除列表重复项”的主题。 我喜欢HashSet的解决方案。 但是,我只有String []的列表,它将无法使用它。 可能是因为即使两个stringArray相同, stringArray1.equals(stringArray2)也会返回false; 要比较字符串Array,我们必须使用Arrays.equals,而HashSet则不是这种情况。

所以我有一个String[]用户的userList,其中只有2个字符串:username和userID。 由于两者都是链接的(每个用户名只有一个userID),所以仅比较那些字符串中的一个就足够了。

我需要的是一种从列表中删除重复项的快速方法。

我想到了这样的事情:

List<String> userNamesList = new ArrayList<String>();
List<String[]> userListWithoutDuplicates = new ArrayList<String[]>();
for(String[] user : userList){
    if(!userNamesList.contains(user[0])){
        userNamesList.add(user[0]);
        userListWithoutDuplicates.add(user);
    }
}

但是,这需要两个新的List和一个循环(我很确定其他解决方案仍然需要该循环)。

我想知道是否有更好的解决方案。 我认为类似的事情应该已经在某处实现。

编辑:我从SQL查询中得到我的数组。 实际上,我有一个数据库和一些用户。 一个用户将在数据库中搜索对某些条件作出响应的其他用户,数据库将向该用户发送String [] {用户名,用户ID}的列表。 所以我已经有一个用户类,它不仅包含用户名和ID。 每个连接的用户都有一个此类的实例,但是数据库无法访问这些实例,因此她无法发送该实例。 我认为String数组是一个简单的解决方案。 我不认为在某些情况下,在数据库中可以多次引用一个用户,因此可以多次选择一个用户。 这就是为什么我的列表中有重复项。

最好的方法是将从数据库返回的每个用户映射到具有两个提到的字符串usernameuserID 然后应根据您对相等性/重复项的定义来实现hashCodeequals 基于此,有很多方法可以消除重复项。 您可以将所有找到的用户添加到Set或在此类用户的列表中流式传输,然后调用Stream.distinct()将用户减少为唯一的用户:

List<User> distinctUsers = users.stream().distinct().collect(Collectors.toList());

如果需要继续使用当前结构,则不能使用Stream.distinct()因为它将通过字符串数组的对象标识比较字符串数组。 必须明确指定相等性。 我们可以通过以下方式做到这一点:

Function<String[], String> comparingBy = user -> user[1]; // user[1] = ID
List<String[]> distinctUsers = users.stream()
        .collect(Collectors.groupingBy(comparingBy))
        .values().stream()
        .map(u -> u.get(0))
        .collect(Collectors.toList());

这将按Function comapringBy对所有用户进行comapringBy comapringBy应该反映您对平等的定义,因此来自两个相等用户的一个是重复的。 根据Stream.distinct保留在遇到顺序中首先出现的元素 ”。 结果是一个不同的列表,没有重复的列表。

另一个数据类型是提到的Set 创建TreeSet ,还可以显式提供相等性的定义。 我们可以使用与上面相同的comapringBy

Set<String[]> distinctUsers = new TreeSet<>(Comparator.comparing(comparingBy));
distinctUsers.addAll(users);

如果您使用的是Java 8,则可以使用流

String[] arrWithDuplicates = new String[]{"John", "John", "Mary", "Paul"};
String[] arrWithoutDuplicates = Arrays.stream(arrWithDuplicates).distinct().toArray(String[]::new);

arrWithoutDuplicates您将拥有“约翰”,“玛丽”和“保罗”

编辑:将userNamesList转换为HashSet,谢谢@Aris_Kortex。 这可以将复杂度从O(n ^ 2)减少到O(n),因为在HashSet中搜索的复杂度是O(1)。

    Set<String> userSet = new HashSet<>(userNamesList);
    List<String[]> userListWithoutDuplicates = userList.stream()
        .filter(user -> !userSet.contains(user[0]))
        .collect(Collectors.toList());

stream()上的distinct()无济于事,因为它会从流中删除所有重复项:在这种情况下,它将删除第0个元素和第一个元素与其他数组中的对应元素相同的数组的重复项。

但是据我了解,TC仅希望删除名称(第0个元素)包含在某些预定义列表中的那些用户。

我当然认为您应该首先使用Set而不是列表。 我们可以根据您的时间和空间复杂性进行修改,这是您的代码的简单两行答案。

        Set set = new HashSet(userNamesList);
        List<String> list = new ArrayList(set);

一个有效的示例在这里运行: https : //ideone.com/JznZCE这实际上取决于您需要实现什么,并且如果您的用户是唯一的,您应该只获取一个集合而不是一个列表,而且如果不是“ String” ”,该信息包含在用户对象中,因此用户的顺序无需更改,并且可以实现以后通过ID或名称来放置用户。

然后,您可以通过重写用户类的Equals和hashcode方法来使用自定义实现进行比较,从而更改比较equals的方式。

希望这可以帮助!

编辑:如果信息源来自数据库,请参阅如何使用“ DISTINCT”关键字(类似mysql构造)来获取唯一列表,以处理代码之外的逻辑。

您可以使用toMap收集器提供一个自定义的keyMapper函数,该函数用作唯一性测试,然后只需将地图的values用作结果即可。

对于您的唯一性测试,我认为使用索引1(用户ID)而不是索引0(用户名)更有意义。 但是,如果您希望将其改回,请使用arr[0]代替下面的arr[1]

List<String[]> userList = new ArrayList<>();
userList.add(new String[]{"George","123"});
userList.add(new String[]{"George","123"});
userList.add(new String[]{"George","456"});
List<String[]> userListNoDupes = new ArrayList<>(userList.stream()
    .collect(Collectors.toMap(arr-> arr[1], Function.identity(), (a,b)-> a)).values());
for(String[] user: userListNoDupes) {
    System.out.println(Arrays.toString(user));
}

输出:

[乔治123]

[乔治,456]

检查此主题: 从列表中删除重复的元素

您可以将列表转换为一组(不允许重复),然后如果确实需要这种类型的集合,则可以返回列表。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM