簡體   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