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

时间:2018-09-03 10:59:33

标签: java arrays arraylist

我知道有很多关于“删除列表重复项”的主题。我喜欢HashSet的解决方案。但是,我只有String []的列表,它将无法使用它。可能是因为stringArray1.equals(stringArray2)即使两个stringArray相同也将返回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数组是一个简单的解决方案。我不认为在某些情况下,在数据库中可以多次引用一个用户,因此可以多次选择一个用户。这就是为什么我的列表中有重复项。

6 个答案:

答案 0 :(得分:1)

最好的方法是将每个从数据库返回的用户映射到具有两个提到的字符串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应该反映您对平等的定义,因此来自两个相等用户的一个是重复的。根据{{​​1}}“ 保留在遇到顺序中出现的元素”。结果是一个不同的列表,没有重复的列表。

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

comapringBy

答案 1 :(得分:0)

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

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

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

答案 2 :(得分:0)

编辑:感谢@Aris_Kortex,将userNamesList转换为HashSet。这样可以将复杂度从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个元素)包含在某些预定义列表中的用户。

答案 3 :(得分:0)

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

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

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

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

希望这会有所帮助!

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

答案 4 :(得分:0)

您可以使用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]

答案 5 :(得分:-1)

检查此主题:Removing duplicate elements from a List

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