การใช้งาน Order Set ใน Java หรือไม่


103

ถ้าใครมีความคุ้นเคยกับ Objective-C มีคอลเลกชันที่เรียกว่าNSOrderedSetที่ทำหน้าที่เป็นชุดและรายการที่สามารถเข้าถึงได้ในฐานะที่เป็นอาร์เรย์ของคน

มีอะไรแบบนี้ใน Java ไหม?

ฉันได้ยินมาว่ามีคอลเลกชันที่เรียกว่าLinkedHashMapแต่ฉันไม่พบอะไรที่เหมือนกับชุดนี้เลย


ฉันกำลังแก้ไขปัญหาที่คล้ายกันใน c ++ ด้วย NSOrderSet เราสามารถเข้าถึงองค์ประกอบตามลำดับที่เราใส่เข้าไปได้หรือไม่
Vinay

คุณรู้วิธีรับฟังก์ชันข้างต้นใน C ++ หรือไม่? ฉันทำหน้าที่เป็น SET และสามารถเข้าถึงเป็นองค์ประกอบของ Array ได้หรือไม่?
Vinay

คำตอบ:


125

ลองดูที่คลาสLinkedHashSet

จาก Java doc :

ตารางแฮชและการใช้งานรายการที่เชื่อมโยงของอินเทอร์เฟซ Set พร้อมลำดับการทำซ้ำที่คาดเดาได้ การนำไปใช้งานนี้แตกต่างจาก HashSet ตรงที่จะรักษารายการที่เชื่อมโยงแบบทวีคูณซึ่งรันผ่านรายการทั้งหมด รายการที่เชื่อมโยงนี้กำหนดสั่งซื้อซ้ำซึ่งเป็นลำดับที่องค์ประกอบที่ถูกใส่เข้าไปในชุด (แทรกการสั่งซื้อ) โปรดทราบว่าลำดับการแทรกจะไม่ได้รับผลกระทบหากมีการใส่องค์ประกอบเข้าไปในชุดอีกครั้ง (อิลิเมนต์ e ถูกใส่เข้าไปในเซต s อีกครั้งถ้า s.add (e) ถูกเรียกใช้เมื่อ s.contains (e) จะคืนค่าจริงทันทีก่อนที่จะมีการเรียกใช้)


ขอบคุณมาก. มันดูน่าเบื่อLinkedHashMapแต่ฉันไม่พบมันเลย
Uko


33

ทุกชุดมีตัววนซ้ำ () ตัววนซ้ำของ HashSet ปกตินั้นค่อนข้างสุ่ม TreeSet ทำตามลำดับการจัดเรียงตัววนซ้ำLinkedHashSet จะวนซ้ำตามลำดับการแทรก

อย่างไรก็ตามคุณไม่สามารถแทนที่องค์ประกอบใน LinkedHashSet ได้ คุณสามารถลบรายการหนึ่งออกและเพิ่มอีกรายการได้ แต่องค์ประกอบใหม่จะไม่อยู่แทนที่ของเดิม ใน LinkedHashMap คุณสามารถแทนที่ค่าสำหรับคีย์ที่มีอยู่จากนั้นค่าจะยังคงอยู่ในลำดับเดิม

นอกจากนี้คุณไม่สามารถแทรกในตำแหน่งใดตำแหน่งหนึ่งได้

บางทีคุณอาจควรใช้ ArrayList กับการตรวจสอบอย่างชัดเจนเพื่อหลีกเลี่ยงการแทรกรายการที่ซ้ำกัน


ฉันต้องการที่จะสามารถตั้งค่า / รับองค์ประกอบในตำแหน่งที่เฉพาะเจาะจงและรับองค์ประกอบตามลำดับที่ฉันได้เพิ่มเข้าไป มันตะเข็บที่LinkedHashSetควรทำอย่างนั้น ขอบคุณสำหรับการตอบกลับ
Uko

12

ลองดูที่เป็นเอกสารใน Java API มาตรฐาน ข้างๆLinkedHashMapมีไฟล์LinkedHashSet. แต่โปรดทราบว่าลำดับในนั้นคือลำดับการแทรกไม่ใช่ลำดับตามธรรมชาติขององค์ประกอบ และคุณสามารถวนซ้ำตามลำดับนั้นเท่านั้นห้ามเข้าถึงแบบสุ่ม (ยกเว้นโดยการนับขั้นตอนการทำซ้ำ)

นอกจากนี้ยังมีอินเตอร์เฟซที่SortedSetดำเนินการโดยและTreeSet ConcurrentSkipListSetทั้งสองอนุญาตให้ทำซ้ำตามลำดับธรรมชาติขององค์ประกอบหรือ a Comparatorแต่ไม่ใช่การเข้าถึงแบบสุ่มหรือลำดับการแทรก

สำหรับโครงสร้างข้อมูลที่มีทั้งการเข้าถึงที่มีประสิทธิภาพโดยดัชนีและสามารถใช้เกณฑ์ที่ตั้งไว้ได้อย่างมีประสิทธิภาพคุณจะต้องมีรายการข้ามแต่ไม่มีการใช้งานกับฟังก์ชันนั้นใน Java Standard API แม้ว่าฉันมั่นใจว่ามันง่ายที่จะหา ในอินเตอร์เน็ต.


ฉันอาจเข้าใจผิดในความคิดเห็นของคุณ แต่ฉันรู้สึกว่าตั้งแต่ Java 1.6 มีคอลเล็กชันเริ่มต้นหลายรายการตามรายการข้าม (เช่นพูดConcurrentSkipListSetเป็นต้น)
TacticalCoder

@ user988052: ใช่ แต่สิ่งเหล่านี้ไม่ได้ใช้การเข้าถึงแบบสุ่มโดยดัชนี (แม้ว่าความเข้าใจของฉันเกี่ยวกับรายการข้ามบอกว่าควรเป็นไปได้) ซึ่งดูเหมือนว่าจะเป็นสิ่งที่ Uko ต้องการ
Michael Borgwardt

@MichaelBorgwardt Java 6 และใหม่กว่ารวมการใช้งาน Skip List คู่หนึ่ง: ConcurrentSkipListMapและConcurrentSkipListSet. ทั้งสองรักษาการเรียงลำดับตามลำดับธรรมชาติหรือตัวเปรียบเทียบ ฉันไม่เข้าใจว่าพวกเขาให้การเข้าถึงแบบสุ่มหรือลำดับการเข้าที่คุณพูดคุยหรือไม่
Basil Bourque

@BasilBourque: พบดีและขอบคุณสำหรับการแก้ไข OP ต้องการการเข้าถึงโดยดัชนีและตอนนี้ฉันได้ดูและคิดเกี่ยวกับมันฉันคิดว่ารายการข้ามก็ไม่มีความสามารถเช่นกัน ...
Michael Borgwardt

5

ลองใช้java.util.TreeSetอุปกรณ์SortedSetดังกล่าว

ในการอ้างอิงเอกสาร:

"องค์ประกอบจะเรียงลำดับโดยใช้การจัดลำดับตามธรรมชาติหรือโดยตัวเปรียบเทียบที่มีให้ในเวลาสร้างที่กำหนดขึ้นอยู่กับว่าจะใช้ตัวสร้างใด"

โปรดทราบว่าการเพิ่มลบและมีมีบันทึกต้นทุนเวลา (n)

หากคุณต้องการเข้าถึงเนื้อหาของชุดเป็น Array คุณสามารถแปลงได้ดังนี้:

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

อาร์เรย์นี้จะถูกจัดเรียงโดยใช้เกณฑ์เดียวกับ TreeSet (ตามธรรมชาติหรือโดยตัวเปรียบเทียบ) และในหลาย ๆ กรณีสิ่งนี้จะได้เปรียบแทนที่จะทำ Arrays.sort ()


1
ฉันจำเป็นต้องสั่งซื้อเช่นเดียวกับใน ArrayList EI ถ้าผมใส่องค์ประกอบแรกcและองค์ประกอบที่แล้วaที่ผมย้ำกว่าคอลเลกชันที่ฉันต้องการที่จะรับพวกเขาในลำดับเดียวกัน: c, aฯลฯ
Uko

4

TreeSet ได้รับคำสั่ง

http://docs.oracle.com/javase/6/docs/api/java/util/TreeSet.html


นี่คือคำตอบที่ถูกต้อง TreeSet ไม่เหมือน LHSet คือใช้ java.util.SortedSet
vemv

41
สั่งและจัดเรียงเป็นสิ่งที่แตกต่างกัน TreeSet ถูกเรียงลำดับไม่ได้สั่ง
andrii

2
ตามลำดับหมายถึงลำดับการแทรก (วิธีที่รายการ) ทำงานในขณะที่การจัดเรียงหมายถึงการจัดลำดับองค์ประกอบตามความเป็นจริงตามเกณฑ์บางประการ
Cornel Masson

1

treeetเป็นชุดที่สั่งซื้อ แต่คุณไม่สามารถเข้าถึงผ่านดัชนีรายการได้เพียงแค่วนซ้ำหรือไปที่จุดเริ่มต้น / จุดสิ้นสุด


ด้วย treeSet คุณจะต้องเสียค่าใช้จ่ายเพิ่มขึ้น LinkedHashSet มีต้นทุนที่ต่ำกว่า
Carlos

0

หากเรากำลังพูดถึงการใช้งานข้ามรายการราคาไม่แพงฉันสงสัยว่าในแง่ของ O ขนาดใหญ่ค่าใช้จ่ายของการดำเนินการนี้คืออะไร

YourType [] array = someSet.toArray (YourType ใหม่ [yourSet.size ()]);

ฉันหมายความว่ามันมักจะติดอยู่ในการสร้างอาร์เรย์ทั้งหมดดังนั้นจึงเป็น O (n):

java.util.Arrays#copyOf

1
ขึ้นอยู่กับลักษณะการทำงานของตัววนซ้ำและsize()วิธีการของชุดพื้นฐาน ย้ำมักจะมีO(n)ขนาดเป็นปกติO(1)ยกเว้นที่มันConcurrentSkipListSet O(n)
Ian Roberts


0

คุณอาจได้รับประโยชน์จากแผนที่สองทิศทางเช่นBiMapจากGoogle Guava

ด้วย a BiMapคุณสามารถจับคู่จำนวนเต็ม (สำหรับการเข้าถึงดัชนีแบบสุ่ม) กับประเภทวัตถุอื่น ๆ ได้อย่างมีประสิทธิภาพ BiMaps คือหนึ่งต่อหนึ่งดังนั้นจำนวนเต็มใด ๆ ที่กำหนดจะมีองค์ประกอบที่เชื่อมโยงกับมันมากที่สุดและองค์ประกอบใด ๆ ก็มีจำนวนเต็มที่เกี่ยวข้อง มันได้รับการสนับสนุนอย่างชาญฉลาดโดยสองHashTableอินสแตนซ์ดังนั้นจึงใช้หน่วยความจำเกือบสองเท่า แต่มีประสิทธิภาพมากกว่าแบบกำหนดเองListมากพอ ๆ กับการประมวลผลเนื่องจากcontains()(ซึ่งจะถูกเรียกเมื่อมีการเพิ่มรายการเพื่อตรวจสอบว่ามีอยู่แล้วหรือไม่) เป็นเวลาคงที่ และการทำงานแบบขนานเช่นHashSet's ในขณะที่Listการใช้งานช้าลงมาก


0

ฉันมีปัญหาที่คล้ายกัน ฉันไม่ต้องการชุดที่สั่ง แต่มีรายการที่รวดเร็วกว่าindexOf/ contains. เนื่องจากฉันไม่พบอะไรเลยฉันจึงดำเนินการด้วยตัวเอง นี่คือรหัสซึ่งจะใช้ทั้งสองอย่างSetและListแม้ว่าการดำเนินการรายการจำนวนมากทั้งหมดจะไม่เร็วเท่ากับArrayListเวอร์ชัน

ข้อจำกัดความรับผิดชอบ: ไม่ได้ทดสอบ

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(); }
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.