Bất kỳ triển khai của Bộ có thứ tự trong Java?


98

Nếu ai đó đã quen thuộc với Objective-C thì có một tập hợp được gọi NSOrderedSetSet và các mục của nó có thể được truy cập như một Array .

Có điều gì như thế này trong Java không?

Tôi nghe nói rằng có một bộ sưu tập được gọi là LinkedHashMap, nhưng tôi chưa tìm thấy bất cứ thứ gì giống nó cho một bộ.


Tôi đang giải quyết một vấn đề tương tự trong c ++. với NSOrderedSet, chúng ta có thể truy cập các phần tử theo thứ tự đã chèn vào nó không?
Vinay

Bạn có biết làm thế nào để có được chức năng trên trong C ++ không? i, e hoạt động như SET và có thể được truy cập như một phần tử của Mảng?
Vinay

Câu trả lời:


118

Hãy xem lớp LinkedHashSet

Từ tài liệu Java :

Bảng băm và triển khai danh sách liên kết của giao diện Đặt, với thứ tự lặp lại có thể dự đoán được . Việc triển khai này khác với HashSet ở chỗ nó duy trì một danh sách được liên kết kép chạy qua tất cả các mục của nó. Danh sách liên kết này xác định thứ tự lặp lại, là thứ tự mà các phần tử được chèn vào tập hợp (thứ tự chèn) . Lưu ý rằng thứ tự chèn không bị ảnh hưởng nếu một phần tử được chèn lại vào tập hợp . (Một phần tử e được chèn lại vào một tập hợp s nếu s.add (e) được gọi khi s.contains (e) sẽ trả về true ngay lập tức trước khi gọi.).


Cảm ơn rât nhiều. Nhìn vào thì nó có đường nối tầm thường LinkedHashMapnhưng tôi không tìm thấy nó bằng cách nào đó.
Uko


9
Tại sao câu trả lời này lại nhận được nhiều ủng hộ? Đây không phải là câu trả lời cho câu hỏi. Không có chức năng nào trong LinkedHashSetđó cho phép bạn tìm ra chỉ mục của phần tử đó.
searchhengine

31

Mọi Tập hợp đều có một trình lặp (). Trình lặp của HashSet bình thường khá ngẫu nhiên, TreeSet thực hiện theo thứ tự sắp xếp, trình lặp LinkedHashSet lặp theo thứ tự chèn.

Tuy nhiên, bạn không thể thay thế một phần tử trong LinkedHashSet. Bạn có thể xóa một phần tử và thêm phần tử khác, nhưng phần tử mới sẽ không ở vị trí của phần tử gốc. Trong một LinkedHashMap, bạn có thể thay thế một giá trị cho một khóa hiện có và sau đó các giá trị sẽ vẫn theo thứ tự ban đầu.

Ngoài ra, bạn không thể chèn ở một vị trí nhất định.

Có lẽ bạn nên sử dụng ArrayList với một kiểm tra rõ ràng để tránh chèn các bản sao.


Tôi muốn có thể đặt / lấy phần tử ở vị trí cụ thể và lấy chúng theo thứ tự mà tôi đã thêm chúng. Nó nối LinkedHashSetnên làm điều đó. Nhờ trả lời
Uko

11

Hãy xem tài liệu API tiêu chuẩn Java . Ngay bên cạnh LinkedHashMap, có một LinkedHashSet. Nhưng lưu ý rằng thứ tự trong đó là thứ tự chèn, không phải thứ tự tự nhiên của các phần tử. Và bạn chỉ có thể lặp lại theo thứ tự đó, không thực hiện truy cập ngẫu nhiên (ngoại trừ bằng cách đếm các bước lặp).

Ngoài ra còn có một giao diện được SortedSetthực hiện bởi TreeSetConcurrentSkipListSet. Cả hai đều cho phép lặp lại theo thứ tự tự nhiên của các phần tử của chúng hoặc a Comparator, nhưng không phải là thứ tự chèn hoặc truy cập ngẫu nhiên.

Đối với một cấu trúc dữ liệu vừa có khả năng truy cập hiệu quả bằng chỉ mục vừa có thể triển khai hiệu quả tiêu chí đã đặt, bạn sẽ cần một danh sách bỏ qua , nhưng không có triển khai với chức năng đó trong Java Standard API, mặc dù tôi chắc chắn rằng rất dễ tìm thấy một trên mạng.


Tôi có thể hiểu nhầm nhận xét của bạn nhưng tôi có ấn tượng rằng kể từ Java 1.6 đã có một số bộ sưu tập mặc định dựa trên danh sách bỏ qua (chẳng hạn như ConcurrentSkipListSet , v.v.).
TacticalCoder

@ user988052: có, nhưng những thứ đó không thực hiện truy cập ngẫu nhiên theo chỉ mục (mặc dù hiểu biết của tôi về danh sách bỏ qua cho thấy điều đó có thể xảy ra), có vẻ là những gì Uko muốn.
Michael Borgwardt

@MichaelBorgwardt Java 6 trở lên bao gồm một cặp triển khai Danh sách Bỏ qua: ConcurrentSkipListMapConcurrentSkipListSet. Cả hai đều duy trì sắp xếp dựa trên thứ tự tự nhiên hoặc Bộ so sánh. Tôi không hiểu liệu họ có cung cấp quyền truy cập ngẫu nhiên hay thứ tự nhập cảnh mà bạn thảo luận hay không.
Basil Bourque

@BasilBourque: tìm thấy tốt, và cảm ơn vì các chỉnh sửa. OP muốn truy cập bằng chỉ mục, và bây giờ mà tôi ấy nhìn nó và suy nghĩ về nó, tôi nghĩ rằng bỏ qua danh sách thực sự không có khả năng mà một trong hai ...
Michael Borgwardt

5

Đây là câu trả lời chính xác. Không giống như LHSet, TreeSet không thực hiện java.util.SortedSet.
vemv

40
thứ tự và sắp xếp là những thứ khác nhau. TreeSet được sắp xếp, không theo thứ tự
andrii

2
Chính xác, sắp xếp đề cập đến thứ tự chèn (cách một Danh sách) hoạt động, trong khi sắp xếp đề cập đến thứ tự sau thực tế của các phần tử dựa trên một số tiêu chí.
Cornel Masson

5

Hãy thử sử dụng nông cụ java.util.TreeSetđó SortedSet.

Để trích dẫn tài liệu:

"Các phần tử được sắp xếp theo thứ tự tự nhiên của chúng hoặc bởi Bộ so sánh được cung cấp tại thời điểm tạo đã đặt, tùy thuộc vào phương thức khởi tạo nào được sử dụng"

Lưu ý rằng thêm, bớt và chứa có nhật ký chi phí thời gian (n).

Nếu bạn muốn truy cập nội dung của tập hợp dưới dạng Mảng, bạn có thể chuyển đổi nó bằng cách:

YourType[] array = someSet.toArray(new YourType[yourSet.size()]); 

Mảng này sẽ được sắp xếp với cùng tiêu chí như TreeSet (tự nhiên hoặc bởi một bộ so sánh) và trong nhiều trường hợp, điều này sẽ có lợi thế hơn thay vì thực hiện Arrays.sort ()


1
Tôi cần phải ra lệnh như trong ArrayList ei nếu tôi đặt yếu tố đầu tiên cvà sau đó yếu tố a, như tôi lặp qua một bộ sưu tập Tôi muốn để có được chúng theo thứ tự: c, a, vv
Uko

1

treeet là một tập hợp có thứ tự, nhưng bạn không thể truy cập thông qua chỉ mục mục, chỉ cần lặp lại hoặc chuyển đến đầu / cuối.


Với treeSet, bạn sẽ phải chịu chi phí tăng lên. LinkedHashSet có chi phí thấp hơn.
Carlos

0

Nếu chúng ta đang nói về việc triển khai danh sách bỏ qua không tốn kém, thì tôi tự hỏi theo chữ O lớn, chi phí của hoạt động này là bao nhiêu:

YourType [] array = someSet.toArray (YourType mới [yourSet.size ()]);

Ý tôi là nó luôn bị mắc kẹt trong việc tạo toàn bộ mảng, vì vậy nó là O (n):

java.util.Arrays#copyOf

1
Điều đó phụ thuộc vào đặc tính hiệu suất của trình lặp và size()phương thức của tập cơ sở. Sự lặp lại thường là O(n), kích thước thường là O(1)ngoại trừ ConcurrentSkipListSetvị trí của nó O(n).
Ian Roberts


0

Bạn cũng có thể nhận được một số tiện ích từ Bản đồ hai chiều như BiMaptừ Google Guava

Với a BiMap, bạn có thể ánh xạ một cách khá hiệu quả một Số nguyên (để truy cập chỉ mục ngẫu nhiên) với bất kỳ loại đối tượng nào khác. BiMaps là một đối một, do đó, bất kỳ số nguyên nhất định nào cũng có tối đa một phần tử được liên kết với nó và bất kỳ phần tử nào cũng có một số nguyên được liên kết. Nó được củng cố một cách thông minh bởi hai HashTablephiên bản, vì vậy nó sử dụng gần như gấp đôi bộ nhớ, nhưng nó hiệu quả hơn nhiều so với một tùy chỉnh Listvề xử lý vì contains()(được gọi khi một mục được thêm vào để kiểm tra xem nó đã tồn tại chưa) là một thời gian không đổi và hoạt động thân thiện song song như HashSet's, trong khi Listviệc thực hiện chậm hơn RẤT NHIỀU.


0

Tôi đã có một vấn đề tương tự. Tôi không cần một bộ có thứ tự nhưng nhiều hơn một danh sách với nhanh indexOf/ contains. Vì tôi không tìm thấy bất cứ điều gì ngoài đó nên tôi đã tự mình thực hiện một cái. Đây là đoạn mã, nó thực hiện cả hai SetList, mặc dù không phải tất cả các hoạt động danh sách hàng loạt đều nhanh như các ArrayListphiên bản.

tuyên bố từ chối trách nhiệm: không được kiểm tra

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
import java.util.Collection;
import java.util.Comparator;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import static java.util.Objects.requireNonNull;

/**
 * An ArrayList that keeps an index of its content so that contains()/indexOf() are fast. Duplicate entries are
 * ignored as most other java Set's do.
 */
public class IndexedArraySet<E> extends ArrayList<E> implements Set<E> {

    public IndexedArraySet() { super(); }

    public IndexedArraySet(Iterable<E> c) {
        super();
        addAll(c);
    }

    private HashMap<E, Integer> indexMap = new HashMap<>();

    private void reindex() {
        indexMap.clear();
        int idx = 0;
        for (E item: this) {
            addToIndex(item, idx++);
        }
    }

    private E addToIndex(E e, int idx) {
        indexMap.putIfAbsent(requireNonNull(e), idx);
        return e;
    }

    @Override
    public boolean add(E e) {
        if(indexMap.putIfAbsent(requireNonNull(e), size()) != null) return false;
        super.add(e);
        return true;
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
        return addAll((Iterable<? extends E>) c);
    }
    public boolean addAll(Iterable<? extends E> c) {
        boolean rv = false;
        for (E item: c) {
            rv |= add(item);
        }
        return rv;
    }

    @Override
    public boolean contains(Object e) {
        return indexMap.containsKey(e);
    }

    @Override

    public int indexOf(Object e) {
        if (e == null) return -1;
        Integer i = indexMap.get(e);
        return (i == null) ? -1 : i;
    }

    @Override
    public int lastIndexOf(Object e) {
        return indexOf(e);
    }

    @Override @SuppressWarnings("unchecked")
    public Object clone() {
        IndexedArraySet clone = (IndexedArraySet) super.clone();
        clone.indexMap = (HashMap) indexMap.clone();
        return clone;
    }

    @Override
    public void add(int idx, E e) {
        if(indexMap.putIfAbsent(requireNonNull(e), -1) != null) return;
        super.add(idx, e);
        reindex();
    }

    @Override
    public boolean remove(Object e) {
        boolean rv;
        try { rv = super.remove(e); }
        finally { reindex(); }
        return rv;
    }

    @Override
    public void clear() {
        super.clear();
        indexMap.clear();
    }

    @Override
    public boolean addAll(int idx, Collection<? extends E> c) {
        boolean rv;
        try {
            for(E item : c) {
                // check uniqueness
                addToIndex(item, -1);
            }
            rv = super.addAll(idx, c);
        } finally {
            reindex();
        }
        return rv;
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        boolean rv;
        try { rv = super.removeAll(c); }
        finally { reindex(); }
        return rv;
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        boolean rv;
        try { rv = super.retainAll(c); }
        finally { reindex(); }
        return rv;
    }

    @Override
    public boolean removeIf(Predicate<? super E> filter) {
        boolean rv;
        try { rv = super.removeIf(filter); }
        finally { reindex(); }
        return rv;
    }

    @Override
    public void replaceAll(final UnaryOperator<E> operator) {
        indexMap.clear();
        try {
            int duplicates = 0;
            for (int i = 0; i < size(); i++) {
                E newval = requireNonNull(operator.apply(this.get(i)));
                if(indexMap.putIfAbsent(newval, i-duplicates) == null) {
                    super.set(i-duplicates, newval);
                } else {
                    duplicates++;
                }
            }
            removeRange(size()-duplicates, size());
        } catch (Exception ex) {
            // If there's an exception the indexMap will be inconsistent
            reindex();
            throw ex;
        }

    }

    @Override
    public void sort(Comparator<? super E> c) {
        try { super.sort(c); }
        finally { reindex(); }
    }
}
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.