Có triển khai Danh sách không trùng lặp ngoài đó không?


86

Tôi biết về SortedSet, nhưng trong trường hợp của tôi, tôi cần một cái gì đó có thể triển khai List, và không Set. Vì vậy, có một triển khai ngoài đó, trong API hoặc ở nơi khác không?

Không khó để tự thực hiện, nhưng tôi đã hiểu tại sao không hỏi mọi người ở đây trước?


1
Tại sao nó cần phải thực hiện Danh sách? Các bộ có thể lặp lại, giống như danh sách, vì vậy tôi cho rằng phương thức nhận đang thực thi Danh sách vì một số lý do khác.
Rob

@Rob Đúng vậy, đó là một nhu cầu bên ngoài, và cấu trúc dữ liệu bao gồm rất nhiều hơn một Danh sách.
Yuval

Nếu người dùng muốn một danh sách, sau đó rõ ràng là nhu cầu phương pháp của giao diện DANH mà không có mặt um giao diện SET ...
marcolopes

Câu trả lời:


92

Không có bộ sưu tập Java nào trong thư viện chuẩn để thực hiện việc này. LinkedHashSet<E>Tuy nhiên, giữ nguyên thứ tự tương tự như a List, vì vậy nếu bạn bọc tập hợp của mình trong một Listkhi bạn muốn sử dụng nó như một, Listbạn sẽ nhận được ngữ nghĩa bạn muốn.

Ngoài ra, Bộ sưu tập Commons (hoặc commons-collections4, đối với phiên bản chung) có một Listcái thực hiện những gì bạn muốn: SetUniqueList/ SetUniqueList<E>.


5
Lớp học Commons là chính xác những gì tôi cần, nhưng sếp của tôi đã bảo tôi cuối cùng phải tự thực hiện nó. 10x dù sao!
Yuval

5
À tốt, không gì bằng phát minh lại bánh xe! Bây giờ bạn sẽ biết nếu cần một lần nữa, dù sao. bộ sưu tập15 là một thứ khá hữu ích để có được sự chú ý; MultiMaps đặc biệt làm giảm bớt nỗi đau của một cái gì đó mà người ta kết thúc việc triển khai bản thân rất nhiều.
Calum

19
@skaffman: anh ấy không thực sự là một tên ngốc, nhưng đôi khi anh ấy thực hiện những động thái ... tốt, kỳ quặc. Dù sao, tôi sẽ không đưa lỗi vào sản phẩm. Trong thị trường ngày nay, tôi hài lòng với công việc của mình và không muốn đóng sập cửa và đốt cầu, nếu bạn hiểu ý tôi.
Yuval

3
Tôi khá ngạc nhiên khi SetUniqueList không có kiểu tham số hóa.
emeraldhieu

2
Jeffrey: Trên nền tảng di động, hệ thống thường sẽ loại bỏ các lớp không sử dụng, nhưng chắc chắn, có rất nhiều lý do khiến bạn không thể sử dụng một trong những giải pháp "bình thường" này. Luôn phải đánh đổi một số vấn đề và không có giải pháp nào khắc phục được mọi trường hợp.
Calum

14

Đây là những gì tôi đã làm và nó hoạt động.

Giả sử tôi phải ArrayListlàm việc với điều đầu tiên tôi đã làm là tạo một cái mới LinkedHashMap.

LinkedHashSet<E> hashSet = new LinkedHashSet<E>()

Sau đó, tôi cố gắng thêm phần tử mới của mình vào LinkedHashSet. Phương thức add không thay đổi LinkedHasSetvà trả về false nếu phần tử mới là bản sao. Vì vậy, đây trở thành một điều kiện tôi có thể kiểm tra trước khi thêm vào ArrayList.

if (hashSet.add(E)) arrayList.add(E);

Đây là một cách đơn giản và thanh lịch để ngăn các bản sao được thêm vào danh sách mảng. Nếu bạn muốn, bạn có thể đóng gói nó trong và ghi đè phương thức add trong một lớp mở rộng ArrayList. Chỉ cần nhớ xử lý addAllbằng cách lặp qua các phần tử và gọi phương thức add.


1
Vâng, tôi nghĩ, đây là giải pháp tốt nhất cho nó, bạn cũng có thể chỉ cần sử dụng HashSet bình thường, không phải Liên kết và sau đó bạn có thể sử dụng danh sách của mình như bạn muốn, bạn cũng có thể quyết định những việc cần làm trong một số tình huống, như trong thêm một phần tử bên trong danh sách trước một chỉ mục cụ thể, bạn có thể quyết định rằng bạn có muốn di chuyển mục bị trùng lặp đến vị trí này hay không.
gyurix

Giải pháp tốt nhất ở đây ... Sẽ đăng mã lớp UniqueList của tôi
marcolopes Ngày

Điều này đã làm việc cho tôi, trong thuật toán Đồ thị BFS của tôi. Bởi vì tôi đã có một số nút mà tôi thêm vào một Queue (LinkedList) chỉ nếu họ không phải là đã có trong.
Jeancarlo Fontalvo

11

Vì vậy, đây là những gì tôi đã làm cuối cùng. Tôi mong điều này giúp được người nào khác.

class NoDuplicatesList<E> extends LinkedList<E> {
    @Override
    public boolean add(E e) {
        if (this.contains(e)) {
            return false;
        }
        else {
            return super.add(e);
        }
    }

    @Override
    public boolean addAll(Collection<? extends E> collection) {
        Collection<E> copy = new LinkedList<E>(collection);
        copy.removeAll(this);
        return super.addAll(copy);
    }

    @Override
    public boolean addAll(int index, Collection<? extends E> collection) {
        Collection<E> copy = new LinkedList<E>(collection);
        copy.removeAll(this);
        return super.addAll(index, copy);
    }

    @Override
    public void add(int index, E element) {
        if (this.contains(element)) {
            return;
        }
        else {
            super.add(index, element);
        }
    }
}   

10
Hãy cẩn thận - LinkedList.contains () cần phải quét toàn bộ danh sách để xác định xem một đối tượng có trong Danh sách hay không. Điều này có nghĩa là khi bạn thêm các đối tượng vào một Danh sách lớn, toàn bộ Danh sách sẽ được quét cho mỗi thao tác thêm (trong trường hợp xấu nhất). Điều này có thể kết thúc là CHẬM.
matt b

8
Ngoài ra, ghi đè addAll của bạn không kiểm tra các bản sao trong bộ sưu tập đang được chuyển cho addAll ().
matt b

@mattb Sau đó, bạn sẽ giải quyết vấn đề này như thế nào: Trên Android, khi liên kết các đối tượng vào chế độ xem mục danh sách, chúng tôi được cung cấp vị trí của mục trong bộ điều hợp chế độ xem. Vì các tập hợp không có chỉ mục, nên cách duy nhất là kiểm tra xem đối tượng có tồn tại hay không khi sử dụng danh sách là lặp lại và tìm kiếm một bản sao hiện có.
TheRealChx101

6

Tại sao không đóng gói một tập hợp bằng một danh sách, sắp xếp như sau:

new ArrayList( new LinkedHashSet() )

Điều này để lại việc triển khai khác cho một người thực sự thành thạo về Bộ sưu tập ;-)


4
Hàm tạo này sao chép nội dung của Tập hợp vào Danh sách mới, thay vì gói nó.
Calum

@Calum, đúng vậy, nhưng thay vì lo lắng về việc không thêm các bản sao vào Danh sách, anh ta có thể thêm các đối tượng của mình vào một Tập hợp (và để cho Tập hợp lo lắng về việc lọc ra các bản sao) và chỉ cần bọc Tập hợp đó trong một Danh sách khi chuyển nó đến phương pháp bên ngoài.
matt b

4
Điều này sao chép một tập hợp vào một danh sách nhưng bạn không có bất kỳ thứ tự nổi tiếng nào. Nhưng đây là những gì câu hỏi là tất cả về.
JANNING

4

Bạn nên nghiêm túc xem xét câu trả lời của dhiller:

  1. Thay vì lo lắng về việc thêm các đối tượng của bạn vào một Danh sách ít trùng lặp, hãy thêm chúng vào một Tập hợp (bất kỳ triển khai nào), về bản chất sẽ lọc ra các bản sao.
  2. Khi bạn cần gọi phương thức yêu cầu Danh sách, hãy bọc nó trong dấu new ArrayList(set)(hoặc a new LinkedList(set), bất kỳ).

Tôi nghĩ rằng giải pháp bạn đã đăng cùng với NoDuplicatesListmột số vấn đề, chủ yếu là với contains()phương thức, cộng với lớp của bạn không xử lý việc kiểm tra các bản sao trong Bộ sưu tập được chuyển đến addAll()phương thức của bạn .


Tôi muốn tìm hiểu trong số này có () các vấn đề. Đối với addAll (), tôi tạo một bản sao của bộ sưu tập đã cho và xóa tất cả các đối tượng đã có trong 'this'. Làm thế nào để điều đó không xử lý các bản sao?
Yuval

Như tôi đã đề cập trong nhận xét của tôi cho bài đăng trên lớp của bạn, hàm chứa () phải quét toàn bộ danh sách (trong trường hợp xấu nhất) để tìm xem đối tượng có trong danh sách hay không. Nếu bạn có một danh sách gồm 1 triệu mục và thêm 10 vào riêng lẻ, thì (trong trường hợp xấu nhất) hơn mười triệu mục sẽ được quét.
matt b

Đối với addAll (), nếu Bộ sưu tập được chuyển đến addAll chứa bản thân các bản sao, chúng sẽ không được phát hiện. Ví dụ: danh sách {A, B, C, D} danh sách tham số {B, D, E, E, E} của bạn. Bạn tạo một bản sao của tham số và sau khi removeTất cả nó chứa {E, E, E}.
matt b

Vấn đề addAll () không thực sự liên quan đến tôi, vì tôi sử dụng NoDuplicatesList trong suốt quy trình và addAll () sẽ nhận một NoDuplicatesList khác làm tham số của nó. Bạn sẽ đề xuất điều gì để cải thiện hiệu suất chứa ()?
Yuval

3

Tôi cần một cái gì đó như vậy, vì vậy tôi đã đi đến các bộ sưu tập commons và sử dụng SetUniqueList, nhưng khi tôi chạy một số kiểm tra hiệu suất, tôi thấy rằng nó có vẻ không được tối ưu hóa so với trường hợp nếu tôi muốn sử dụng a Setvà lấy một Arraybằng cách sử dụng Set.toArray()phương thức.

Các SetUniqueTestmất 20: 1 thời gian để điền vào và sau đó đi qua 100.000 Strings so với việc thực hiện khác, đó là một sự khác biệt to tát.

Vì vậy, nếu bạn lo lắng về hiệu suất, tôi khuyên bạn nên sử dụng Đặt và Lấy một mảng thay vì sử dụng SetUniqueList, trừ khi bạn thực sự cần logic của SetUniqueList, khi đó bạn sẽ cần kiểm tra các giải pháp khác ...

Phương pháp chính của mã kiểm tra :

public static void main(String[] args) {


SetUniqueList pq = SetUniqueList.decorate(new ArrayList());
Set s = new TreeSet();

long t1 = 0L;
long t2 = 0L;
String t;


t1 = System.nanoTime();
for (int i = 0; i < 200000; i++) {
    pq.add("a" + Math.random());
}
while (!pq.isEmpty()) {
    t = (String) pq.remove(0);
}
t1 = System.nanoTime() - t1;

t2 = System.nanoTime();
for (int i = 0; i < 200000; i++) {
    s.add("a" + Math.random());
}

s.clear();
String[] d = (String[]) s.toArray(new String[0]);
s.clear();
for (int i = 0; i < d.length; i++) {
    t = d[i];

}
t2 = System.nanoTime() - t2;

System.out.println((double)t1/1000/1000/1000); //seconds
System.out.println((double)t2/1000/1000/1000); //seconds
System.out.println(((double) t1) / t2);        //comparing results

}

Trân trọng, Mohammed Sleem


1

Chú ý: nó không mất sublist thực hiện vào tài khoản.

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

public class UniqueList<T> extends ArrayList<T> {

    private static final long serialVersionUID = 1L;

    /** Unique elements SET */
    private final Set<T> set=new HashSet();

    /** Used by addAll methods */
    private Collection<T> addUnique(Collection<? extends T> col) {
        Collection<T> unique=new ArrayList();
        for(T e: col){
            if (set.add(e)) unique.add(e);
        }
        return unique;
    }

    @Override
    public boolean add(T e) {
        return set.add(e) ? super.add(e) : false;
    }

    @Override
    public boolean addAll(Collection<? extends T> col) {
        return super.addAll(addUnique(col));
    }

    @Override
    public void add(int index, T e) {
        if (set.add(e)) super.add(index, e);
    }

    @Override
    public boolean addAll(int index, Collection<? extends T> col) {
        return super.addAll(index, addUnique(col));
    }

}

0

Các tài liệu hướng dẫn cho các giao diện bộ sưu tập nói:

Tập hợp - tập hợp không được chứa các phần tử trùng lặp.
Danh sách - một tập hợp có thứ tự (đôi khi được gọi là một chuỗi). Danh sách có thể chứa các phần tử trùng lặp.

Vì vậy, nếu bạn không muốn trùng lặp, bạn có thể không nên sử dụng danh sách.


Tôi đã đề cập cụ thể rằng tôi cần triển khai Danh sách. Tin tôi đi, có lý do.
Yuval

Có phải lý do là vì bạn đang tương tác với một API đang sử dụng Danh sách làm tham số (thay vì Bộ sưu tập) không? Đó là một chút khó chịu khi phải đối phó với
matt b

Trên thực tế, API lấy một Map <AccountType, Map <AccountType, List <Account> >>, có nghĩa là giữ một nơi nào đó trong vùng lân cận của hàng chục đến hàng trăm danh sách ... bah.
Yuval

Việc xây dựng các hàm xác suất với các cặp phần tử-xác suất không được phép có các phần tử trùng lặp, mặc dù các phần tử trùng lặp chỉ có thể được hợp nhất.
Al G Johnston

-1

trong addphương pháp, tại sao không sử dụng HashSet.add()để kiểm tra các bản sao thay vì HashSet.consist(). HashSet.add()sẽ trả lại truenếu không có bản sao và falsenếu không.


HashSet#consist()gì?
naXa

-1

Trên đầu tôi, danh sách cho phép trùng lặp. Bạn có thể nhanh chóng triển khai a UniqueArrayListvà ghi đè tất cả các chức năng add/ insertcần kiểm tra contains()trước khi bạn gọi các phương thức kế thừa. Đối với mục đích sử dụng cá nhân, bạn chỉ có thể triển khai addphương pháp bạn sử dụng và ghi đè các phương pháp khác để đưa ra một ngoại lệ trong trường hợp các lập trình viên trong tương lai cố gắng sử dụng danh sách theo cách khác.


Tôi đã sẵn sàng quay trở lại ý tưởng này (mà cuối cùng tôi phải làm) nếu không có ai đề xuất điều gì tốt hơn = 8-) Xem câu trả lời của riêng tôi ở trên.
Yuval

-3

Tôi vừa tạo UniqueList của riêng mình trong thư viện nhỏ của riêng tôi như thế này:

package com.bprog.collections;//my own little set of useful utilities and classes

import java.util.HashSet;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Jonathan
*/
public class UniqueList {

private HashSet masterSet = new HashSet();
private ArrayList growableUniques;
private Object[] returnable;

public UniqueList() {
    growableUniques = new ArrayList();
}

public UniqueList(int size) {
    growableUniques = new ArrayList(size);
}

public void add(Object thing) {
    if (!masterSet.contains(thing)) {
        masterSet.add(thing);
        growableUniques.add(thing);
    }
}

/**
 * Casts to an ArrayList of unique values
 * @return 
 */
public List getList(){
    return growableUniques;
}

public Object get(int index) {
    return growableUniques.get(index);
}

public Object[] toObjectArray() {
    int size = growableUniques.size();
    returnable = new Object[size];
    for (int i = 0; i < size; i++) {
        returnable[i] = growableUniques.get(i);
    }
    return returnable;
    }
}

Tôi có một lớp TestCollections trông giống như sau:

package com.bprog.collections;
import com.bprog.out.Out;
/**
*
* @author Jonathan
*/
public class TestCollections {
    public static void main(String[] args){
        UniqueList ul = new UniqueList();
        ul.add("Test");
        ul.add("Test");
        ul.add("Not a copy");
        ul.add("Test"); 
        //should only contain two things
        Object[] content = ul.toObjectArray();
        Out.pl("Array Content",content);
    }
}

Hoạt động tốt. Tất cả những gì nó làm là nó thêm vào một tập hợp nếu nó chưa có và có một Danh sách mảng có thể trả về, cũng như một mảng đối tượng.


Vâng, bạn nên thêm một chút phương pháp vào nó để triển khai giao diện Danh sách.
gyurix
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.