มีการใช้งานรายการที่ไม่ซ้ำกันหรือไม่?


87

ฉันรู้เกี่ยวกับSortedSetแต่ในกรณีของฉันฉันต้องการสิ่งที่นำไปปฏิบัติและไม่List Setดังนั้นจึงมีการนำไปใช้งานใน API หรือที่อื่น ๆ หรือไม่?

ไม่น่าจะยากที่จะนำไปใช้เอง แต่ฉันคิดว่าทำไมไม่ถามคนที่นี่ก่อน


1
ทำไมจึงต้องใช้ List? ชุดสามารถทำซ้ำได้เช่นรายการดังนั้นฉันคิดว่าวิธีการรับกำลังบังคับใช้รายการด้วยเหตุผลอื่น
Rob

@Rob ถูกต้องมันเป็นความต้องการภายนอกและโครงสร้างข้อมูลรวมถึงนรกของมากกว่าหนึ่งรายการ
Yuval

หากผู้ใช้ต้องการ LIST ก็เป็นที่ชัดเจนว่าต้องการวิธีการของอินเทอร์เฟซ LIST ที่ไม่มีอยู่ในอินเทอร์เฟซ SET ...
marcolopes

คำตอบ:


94

ไม่มีคอลเลกชัน Java ในไลบรารีมาตรฐานที่จะทำสิ่งนี้ LinkedHashSet<E>เก็บรักษาคำสั่งเช่นเดียวกับ a Listดังนั้นหากคุณห่อชุดของคุณในListเวลาที่คุณต้องการใช้เป็นListคุณจะได้รับความหมายที่คุณต้องการ

หรืออีกวิธีหนึ่งCommons Collections (หรือcommons-collections4สำหรับรุ่นทั่วไป) มีListสิ่งที่คุณต้องการอยู่แล้ว: SetUniqueList/ SetUniqueList<E>.


6
คลาส Commons เป็นสิ่งที่ฉันต้องการ แต่เจ้านายของฉันบอกให้ฉันนำไปใช้ด้วยตัวเองในที่สุด 10x ต่อไป!
Yuval

5
อืมไม่มีอะไรเหมือนกับการคิดค้นล้อใหม่! ตอนนี้คุณจะรู้แล้วว่ามีความจำเป็นเกิดขึ้นอีกหรือไม่ คอลเลกชัน 15 เป็นสิ่งที่มีประโยชน์มากในการเตะ MultiMaps โดยเฉพาะอย่างยิ่งช่วยบรรเทาความเจ็บปวดของบางสิ่งบางอย่างจบลงด้วยการใช้งานด้วยตนเอง
Calum

19
@skaffman: เขาไม่ใช่คนงี่เง่า แต่บางครั้งเขาก็เคลื่อนไหวที่ ... ดีแปลก อย่างไรก็ตามฉันจะไม่แนะนำข้อบกพร่องในผลิตภัณฑ์ ในตลาดปัจจุบันฉันมีความสุขกับงานของฉันและไม่ต้องการปิดประตูกระแทกและเผาสะพานถ้าคุณเข้าใจฉัน
Yuval

3
ฉันค่อนข้างแปลกใจเมื่อ SetUniqueList ไม่มีประเภทพารามิเตอร์
emeraldhieu

2
เจฟฟรีย์: บนแพลตฟอร์มมือถือโดยปกติระบบจะลบคลาสที่ไม่ได้ใช้ออกไป แต่แน่นอนว่ามีเหตุผลมากมายที่คุณอาจไม่เลือกวิธีแก้ปัญหา "ปกติ" อย่างใดอย่างหนึ่งเหล่านี้ มีการแลกเปลี่ยนอยู่เสมอและจะไม่มีทางแก้ไขในทุกกรณี
Calum

14

นี่คือสิ่งที่ฉันทำและได้ผล

สมมติว่าผมมีการทำงานกับสิ่งแรกที่ผมทำถูกสร้างขึ้นใหม่ArrayListLinkedHashMap

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

จากนั้นฉันพยายามเพิ่มองค์ประกอบใหม่ของฉันในไฟล์LinkedHashSet. วิธีการเพิ่มจะไม่แก้ไขLinkedHasSetและส่งคืนเท็จหากองค์ประกอบใหม่ซ้ำกัน ดังนั้นนี่จึงกลายเป็นเงื่อนไขที่ฉันสามารถทดสอบได้ก่อนที่จะเพิ่มไฟล์ArrayList.

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

นี่เป็นวิธีง่ายๆและสวยงามในการป้องกันไม่ให้มีการเพิ่มรายการที่ซ้ำกันในรายการอาร์เรย์ หากคุณต้องการคุณสามารถห่อหุ้มมันในและแทนที่เมธอด add ในคลาสที่ขยายArrayList. อย่าลืมจัดการaddAllโดยการวนลูปผ่านองค์ประกอบและเรียกใช้วิธีการเพิ่ม


1
ใช่ฉันคิดว่านี่เป็นทางออกที่ดีที่สุดสำหรับมันคุณยังสามารถใช้ HashSet ปกติไม่ใช่ลิงก์จากนั้นคุณสามารถใช้รายการของคุณได้ตามที่คุณต้องการคุณยังสามารถตัดสินใจว่าจะทำอย่างไรในบางสถานการณ์เช่นใน การเพิ่มองค์ประกอบภายในรายการก่อนดัชนีเฉพาะคุณสามารถกำหนดได้ว่าคุณต้องการย้ายรายการที่ซ้ำกันไปยังตำแหน่งนี้หรือไม่
gyurix

ทางออกที่ดีที่สุดที่นี่ ... จะโพสต์รหัสชั้นเรียน UniqueList ของฉัน
marcolopes

สิ่งนี้ใช้ได้ผลสำหรับฉันในอัลกอริทึม BFS Graph ของฉัน เนื่องจากฉันมีโหนดบางโหนดที่ฉันเพิ่มลงในคิว (LinkedList) หากยังไม่ได้เข้ามา
Jeancarlo Fontalvo

11

นี่คือสิ่งที่ฉันทำในที่สุด ฉันหวังว่านี่จะช่วยคนอื่นได้

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
โปรดระวัง - LinkedList.contains () จำเป็นต้องสแกนรายการทั้งหมดเพื่อตรวจสอบว่ามีวัตถุอยู่ในรายการหรือไม่ ซึ่งหมายความว่าเมื่อคุณเพิ่มวัตถุลงใน List ขนาดใหญ่ระบบจะสแกน List ทั้งหมดสำหรับการดำเนินการเพิ่มแต่ละครั้ง (ในกรณีที่แย่ที่สุด) สิ่งนี้สามารถจบลงด้วยการช้า
matt b

8
นอกจากนี้การแทนที่ addAll ของคุณจะไม่ตรวจหารายการที่ซ้ำกันในคอลเล็กชันที่ส่งผ่านไปยัง addAll ()
แมตต์ข

@mattb คุณจะแก้ปัญหานี้อย่างไร: บน Android เมื่อผูกวัตถุเข้ากับมุมมองรายการเราจะได้รับตำแหน่งของรายการในอะแดปเตอร์มุมมอง เนื่องจากชุดไม่มีดัชนีวิธีเดียวคือการตรวจสอบว่ามีวัตถุอยู่หรือไม่เมื่อใช้รายการคือการวนซ้ำและค้นหาสำเนาที่มีอยู่
TheRealChx101

6

ทำไมไม่ห่อหุ้มชุดด้วยรายการเรียงลำดับดังนี้:

new ArrayList( new LinkedHashSet() )

สิ่งนี้ทำให้การนำไปใช้งานอื่น ๆ สำหรับผู้ที่เป็นผู้เชี่ยวชาญในคอลเล็กชันจริง


4
ตัวสร้างนี้คัดลอกเนื้อหาของ Set ลงในรายการใหม่แทนที่จะรวมไว้
Calum

@Calum ถูกต้อง แต่แทนที่จะกังวลเกี่ยวกับการไม่เพิ่มรายการที่ซ้ำกันในรายการเขาสามารถเพิ่มวัตถุของเขาลงในชุด (และปล่อยให้ Set กังวลเกี่ยวกับการกรองรายการที่ซ้ำกัน) และเพียงแค่รวมชุดนั้นไว้ในรายการเมื่อส่งต่อไปยัง วิธีการภายนอก
matt b

4
สิ่งนี้จะคัดลอกชุดไปยังรายการ แต่คุณไม่มีคำสั่งซื้อที่เป็นที่รู้จัก แต่นี่คือสิ่งที่เป็นคำถามเกี่ยวกับ
ม.ค.

4

คุณควรพิจารณาคำตอบของ dhiller อย่างจริงจัง:

  1. แทนที่จะกังวลเกี่ยวกับการเพิ่มวัตถุของคุณไปยังรายการที่ไม่ซ้ำกันให้เพิ่มลงในชุด (การใช้งานใด ๆ ) ซึ่งโดยธรรมชาติจะกรองรายการที่ซ้ำกันออกไป
  2. เมื่อคุณต้องการเรียกเมธอดที่ต้องใช้ List ให้ห่อด้วยnew ArrayList(set)(หรือnew LinkedList(set)อะไรก็ได้)

ฉันคิดว่าวิธีแก้ปัญหาที่คุณโพสต์พร้อมกับNoDuplicatesListปัญหาบางอย่างส่วนใหญ่เป็นcontains()วิธีการบวกกับชั้นเรียนของคุณไม่สามารถจัดการกับการตรวจสอบรายการที่ซ้ำกันในคอลเล็กชันที่ส่งไปยังaddAll()วิธีการของคุณ


ฉันชอบที่จะเรียนรู้เกี่ยวกับปัญหาเหล่านี้มี () สำหรับ addAll () ฉันสร้างสำเนาของคอลเลกชันที่กำหนดและลบวัตถุทั้งหมดที่มีอยู่แล้วใน 'this' จะไม่จัดการกับรายการที่ซ้ำกันได้อย่างไร
Yuval

ดังที่ฉันได้กล่าวไว้ในความคิดเห็นของฉันในการโพสต์ชั้นเรียนของคุณมี () ต้องสแกนรายการทั้งหมด (ในกรณีที่เลวร้ายที่สุด) เพื่อดูว่ามีวัตถุอยู่ในรายการหรือไม่ หากคุณมีรายการ 1 ล้านรายการและเพิ่ม 10 รายการทีละรายการระบบจะสแกน (ในกรณีเลวร้ายที่สุด) มากกว่าสิบล้านรายการ
แมตต์ข

สำหรับ addAll () หากคอลเลกชันที่ส่งผ่านไปยัง addAll มีรายการที่ซ้ำกันจะไม่ถูกตรวจพบ ตัวอย่างเช่นรายการพารามิเตอร์ {A, B, C, D} ของคุณ {B, D, E, E, E} คุณสร้างสำเนาของพารามิเตอร์และหลังจากลบทั้งหมดแล้วจะมี {E, E, E}
แมตต์ข

ปัญหา addAll () ไม่เกี่ยวข้องกับฉันจริงๆเนื่องจากฉันใช้ NoDuplicatesList ตลอดทั้งโพรซีเดอร์และ addAll () ควรได้รับ NoDuplicatesList อื่นเป็นพารามิเตอร์ คุณจะแนะนำอะไรเพื่อปรับปรุงประสิทธิภาพของ contain ()
Yuval

3

ฉันต้องการอะไรแบบนั้นดังนั้นฉันจึงไปที่คอลเลกชันคอมมอนส์และใช้SetUniqueListแต่เมื่อฉันทำการทดสอบประสิทธิภาพฉันพบว่าดูเหมือนว่าจะไม่ได้รับการปรับให้เหมาะสมที่สุดเมื่อเทียบกับเคสถ้าฉันต้องการใช้ a SetและรับArrayโดยใช้Set.toArray()วิธีการ

SetUniqueTestเอา20: 1 เวลาในการกรอกข้อมูลการสำรวจแล้ว100,000 Stringsเมื่อเทียบกับการดำเนินการอื่น ๆ ซึ่งเป็นความแตกต่างเรื่องใหญ่

ดังนั้นหากคุณกังวลเกี่ยวกับประสิทธิภาพฉันขอแนะนำให้คุณใช้Set and Get an Arrayแทนการใช้SetUniqueListถ้าคุณไม่ต้องการตรรกะจริงๆSetUniqueListคุณจะต้องตรวจสอบโซลูชันอื่น ๆ ...

วิธีการทดสอบรหัสหลัก :

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

}

ขอแสดงความนับถือ Mohammed Sleem


1

หมายเหตุ: ไม่ได้คำนึงถึงการนำรายการย่อยมาใช้

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

เอกสารสำหรับการเชื่อมต่อคอลเลกชันกล่าวว่า:

Set - คอลเล็กชันที่ไม่สามารถมีองค์ประกอบที่ซ้ำกันได้
รายการ - คอลเลกชันที่สั่งซื้อ (บางครั้งเรียกว่าลำดับ) รายการอาจมีองค์ประกอบที่ซ้ำกัน

ดังนั้นหากคุณไม่ต้องการซ้ำกันคุณอาจไม่ควรใช้รายการ


ฉันพูดเป็นพิเศษว่าฉันต้องการการใช้งานรายการ เชื่อฉันมีเหตุผล
Yuval

สาเหตุเป็นเพราะคุณกำลังโต้ตอบกับ API ที่ใช้ List เป็นพารามิเตอร์ (แทนที่จะเป็น Collection) เป็นเรื่องน่ารำคาญเล็กน้อยที่ต้องจัดการ
Matt b

จริงๆแล้ว API ใช้แผนที่ <AccountType, Map <AccountType, List <Account> >> ซึ่งหมายถึงการถือครองที่ไหนสักแห่งในบริเวณใกล้เคียงกับรายการนับสิบถึงหลายร้อยรายการ ...
Yuval

การสร้างฟังก์ชันความน่าจะเป็นที่มีคู่ความน่าจะเป็นขององค์ประกอบต้องเกี่ยวข้องกับการไม่มีรายการที่ซ้ำกันแม้ว่าองค์ประกอบที่ซ้ำกันจะสามารถผสานได้
Al G Johnston

-1

ในaddวิธีการทำไมไม่ใช้HashSet.add()เพื่อตรวจสอบรายการที่ซ้ำกันแทนที่จะเป็นHashSet.consist(). HashSet.add()จะกลับมาtrueหากไม่มีการทำซ้ำหรือfalseอย่างอื่น


คืออะไรHashSet#consist()?
naXa

-1

จากด้านบนของหัวของฉันรายการอนุญาตให้ทำซ้ำ คุณสามารถใช้งานUniqueArrayListและแทนที่ฟังก์ชันadd/ ทั้งหมดinsertเพื่อตรวจสอบได้อย่างรวดเร็วcontains()ก่อนที่คุณจะเรียกใช้วิธีการที่สืบทอด สำหรับการใช้งานส่วนตัวคุณสามารถใช้addวิธีการที่คุณใช้เท่านั้นและแทนที่วิธีอื่น ๆ เพื่อให้เกิดข้อยกเว้นในกรณีที่โปรแกรมเมอร์ในอนาคตพยายามใช้รายการในลักษณะอื่น


ฉันพร้อมที่จะถอยกลับไปสู่ความคิดนี้ (ซึ่งในที่สุดฉันก็ต้อง) ถ้าไม่มีใครแนะนำอะไรที่ดีกว่า = 8-) ดูคำตอบของฉันเองด้านบน
Yuval

-3

ฉันเพิ่งสร้าง UniqueList ของตัวเองในห้องสมุดเล็ก ๆ ของฉันเองดังนี้:

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;
    }
}

ฉันมีคลาส TestCollections ที่มีลักษณะดังนี้:

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

ใช้งานได้ดี เพียงแค่เพิ่มเข้าไปในชุดหากยังไม่มีและมี Arraylist ที่สามารถส่งคืนได้เช่นเดียวกับอาร์เรย์วัตถุ


ใช่คุณควรเพิ่มวิธีการอีกเล็กน้อยเพื่อใช้อินเทอร์เฟซรายการ
gyurix
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.