2008-11-06 45 views
72

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ì đó triển khai List và không phải Set. Vì vậy, có một thực hiện ra khỏi đó, trong API hoặc ở nơi khác?Có triển khai Danh sách không trùng lặp không?

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

+0

Tại sao cần triển khai Danh sách? Các bộ có thể lặp lại, 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

+0

@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 một địa ngục nhiều hơn một Danh sách. – Yuval

+0

Nếu người dùng muốn một DANH SÁCH, thì rõ ràng là cần các phương thức của giao diện LIST không có mặt giao diện SET ... – marcolopes

Trả lời

75

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. Tuy nhiên, LinkedHashSet<E> giữ nguyên thứ tự tương tự như List, vì vậy nếu bạn quấn bộ của mình thành List khi bạn muốn sử dụng nó làm List bạn sẽ nhận được ngữ nghĩa bạn muốn.

Ngoài ra, Commons Collections (hoặc commons-collections4, cho phiên bản generic) có một List mà làm những gì bạn muốn đã: SetUniqueList/SetUniqueList<E>.

+5

Lớp Commons là chính xác những gì tôi cần, nhưng ông chủ của tôi nói với tôi để thực hiện nó bản thân mình cuối cùng. 10x anyway! – Yuval

+3

Ah tốt, không có gì giống như phát minh lại bánh xe! Bạn sẽ biết bây giờ nếu nhu cầu đi lên một lần nữa, anyway. collections15 là một điều khá hữu ích để có đá xung quanh; MultiMaps đặc biệt giảm bớt nỗi đau của một cái gì đó mà kết thúc việc tự mình thực hiện rất nhiều. – Calum

+19

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

4

Tại sao không gói gọn một bộ với một danh sách, sắp xếp như sau:

new ArrayList(new LinkedHashSet()) 

này khiến việc thực hiện khác cho những người là một bậc thầy thực sự của bộ sưu tập ;-)

+2

Hàm khởi tạo này sao chép nội dung của Đặt vào Danh sách mới, chứ không phải gói nó. – Calum

+0

@Calum, đó là chính xác, nhưng thay vì lo lắng về việc không thêm các bản sao vào một Danh sách, anh ta có thể thêm các đối tượng của mình vào một Set (và để cho Set lo lắng về việc lọc ra các bản sao) và chỉ cần bọc Set đó trong Danh sách khi truyền nó sang phương thức bên ngoài. –

+3

Bản sao này được đặt thành 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 câu hỏi này là gì. – Janning

0

Các documentation for collection interfaces nói:

Đặt - bộ sưu tập không thể chứa các phần tử trùng lặp.
Danh sách - bộ sưu tập được sắp xếp (đôi khi được gọi là 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, có thể bạn không nên sử dụng danh sách.

+0

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

+0

Lý do là vì bạn đang tương tác với một API đang lấy Danh sách làm thông số (thay vì một Bộ sưu tập)? Đó là một chút khó chịu để phải đối phó với –

+0

Thực ra API có một Bản đồ >>, có nghĩa là đang nắm 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

0

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

+0

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

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); 
     } 
    } 
} 
+8

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ó được chứa 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 đượ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 bằng SLOW. –

+7

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 được chuyển đến addAll(). –

5

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

  1. Thay vì lo lắng về việc thêm các đối tượng thành một danh sách trùng lặp-ít hơn, thêm chúng vào một Set (bất kỳ thực hiện), mà sẽ bằng cách lọc tự nhiên 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ó theo số new ArrayList(set) (hoặc new LinkedList(set), bất cứ điều gì).

Tôi nghĩ rằng giải pháp bạn được đăng với NoDuplicatesList có một số vấn đề, chủ yếu là với phương pháp , cộng với lớp học của bạn không xử lý kiểm tra các bản sao trong bộ sưu tập truyền cho phương pháp addAll() của bạn.

+0

Tôi rất muốn tìm hiểu về các vấn đề này chứa(). Đố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 mà không xử lý các bản sao? – Yuval

+0

Như tôi đã đề cập trong bình luận của tôi cho bài đăng trên lớp của bạn, 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ó nằm trong danh sách hay không. Nếu bạn có danh sách 1 triệu mục và thêm 10 mục riêng lẻ, thì (trong trường hợp xấu nhất) hơn 10 triệu mục được quét. –

+0

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

2

Tôi cần một cái gì đó như vậy, vì vậy tôi đã đi đến bộ sưu tập commons và sử dụng SetUniqueList, nhưng khi tôi chạy một số thử nghiệm 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 một Set và một mảng bằng cách sử dụng phương thức Set.toArray(), SetUniqueTest mất 20: 1 thời gian để điền và sau đó đi qua 100.000 chuỗi so với triển khai khác, đó là một sự khác biệt lớn, 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 Set và có được một mảng thay vì sử dụng SetUniqueList, trừ khi bạn thực sự cần logic của SetUniqueList, sau đó bạn cần phải kiểm tra các giải pháp khác ...

việc thử nghiệm phương pháp mã chính:

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 Mohammed Sleem http://abusleem.net/blog

-3

Tôi chỉ cần thực hiện UniqueList của riêng tôi trong thư viện nhỏ cho riêng mình 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ó 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ả nó làm là nó thêm vào một bộ nếu nó không có nó đã có và có một Arraylist đó là trả về, cũng như một mảng đối tượng.

+2

Điều này không thực hiện Danh sách – Eva

+0

Vâng, bạn nên thêm một số phương thức khác vào nó để triển khai giao diện Danh sách. – gyurix

0

trong phương thức add, tại sao không sử dụng HashSet.add() để kiểm tra trùng lặp thay vì HashSet.consist(). HashSet.add() sẽ trả về true nếu không có trùng lặp và false nếu không.

+0

'HashSet # include()' là gì? – naXa

9

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

Giả sử tôi có ArrayList để làm việc với điều đầu tiên tôi làm đã được tạo mới LinkedHashMap.

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

Sau đó, tôi cố thêm phần tử mới vào LinkedHashSet. Phương thức thêm không thay đổi LinkedHasSet và trả về false nếu phần tử mới trùng lặp. Vì vậy, điều nà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à cách đơn giản và thanh lịch để ngăn trùng lặp được thêm vào danh sách mảng. Nếu bạn muốn, bạn có thể gói gọn và ghi đè phương thức thêm vào trong một lớp mở rộng ArrayList. Chỉ cần nhớ xử lý addAll bằng cách lặp qua các phần tử và gọi phương thức thêm.

+1

Vâng, tôi nghĩ, đây là giải pháp tốt nhất cho nó, bạn cũng có thể sử dụng một HashSet bình thường, không phải là Linked, 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ể làm gì trong một số tình huống, khi thêm phần tử bên trong danh sách trước một chỉ mục cụ thể, bạn có thể cho rằng bạn muốn di chuyển mục được sao chép sang vị trí này hay không. – gyurix

+0

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

+0

Điều này làm việc cho tôi, trong thuật toán BFS Graph của tôi. Bởi vì tôi đã có một số nút mà tôi đã thêm vào một Hàng đợi (LinkedList) chỉ khi chúng chưa có trong đó. –

0

LƯU Ý: không mất danh sách phụ thực hiệ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)); 
    } 

} 
Các vấn đề liên quan