รับองค์ประกอบจากชุด


323

เหตุใดจึงไม่มีSetการดำเนินการเพื่อให้ได้องค์ประกอบที่เท่ากับอีกองค์ประกอบหนึ่ง

Set<Foo> set = ...;
...
Foo foo = new Foo(1, 2, 3);
Foo bar = set.get(foo);   // get the Foo element from the Set that equals foo

ฉันสามารถถามได้ว่าSetมีองค์ประกอบเท่ากับbarหรือไม่ทำไมฉันจึงไม่สามารถรับองค์ประกอบนั้นได้ :(

เพื่อชี้แจงequalsวิธีการที่ถูกแทนที่ แต่มันจะตรวจสอบเพียงหนึ่งในเขตข้อมูลไม่ได้ทั้งหมด ดังนั้นFooวัตถุสองชิ้นที่ถือว่าเท่ากันอาจมีค่าแตกต่างกันนั่นคือสาเหตุที่ฉันไม่สามารถใช้งานfooได้


2
โพสต์นี้มีการพูดคุยกันอย่างกว้างขวางและได้รับคำแนะนำที่ดี อย่างไรก็ตามหากคุณเพียงแค่มองหาชุดที่สั่งซื้อเพียงใช้SortedSetและการนำไปใช้งานซึ่งเป็นแบบแผนที่ (เช่นTreeSetอนุญาตให้เข้าถึงfirst())
Eliran Malka

3
ฉันพลาดวิธีนั้นเช่นกันสำหรับกรณีเดียวกันกับที่คุณอธิบายไว้ข้างต้น วัตถุประสงค์ -C ( NSSet) มีวิธีการดังกล่าว มันถูกเรียกmemberและส่งคืนวัตถุภายในชุดที่เปรียบเทียบ "เท่ากับ" กับพารามิเตอร์ของmemberวิธีการ (ซึ่งแน่นอนอาจเป็นวัตถุที่แตกต่างกันและยังมีคุณสมบัติที่แตกต่างกันที่เท่ากับอาจไม่ตรวจสอบ)
Mecki

คำตอบ:


118

จะไม่มีจุดรับองค์ประกอบถ้ามันเท่ากัน A Mapเหมาะกว่าสำหรับกรณีนี้


หากคุณยังต้องการค้นหาองค์ประกอบคุณไม่มีตัวเลือกอื่นนอกจากใช้ตัววนซ้ำ:

public static void main(String[] args) {

    Set<Foo> set = new HashSet<Foo>();
    set.add(new Foo("Hello"));

    for (Iterator<Foo> it = set.iterator(); it.hasNext(); ) {
        Foo f = it.next();
        if (f.equals(new Foo("Hello")))
            System.out.println("foo found");
    }
}

static class Foo {
    String string;
    Foo(String string) {
        this.string = string;
    }
    @Override
    public int hashCode() { 
        return string.hashCode(); 
    }
    @Override
    public boolean equals(Object obj) {
        return string.equals(((Foo) obj).string);
    }
}

234
อาจมีจุดที่จะได้รับองค์ประกอบ จะทำอย่างไรถ้าคุณต้องการอัปเดตค่าบางอย่างขององค์ประกอบหลังจากที่เพิ่มไปยังชุดแล้ว ตัวอย่างเช่นเมื่อ. equals () ไม่ได้ใช้ฟิลด์ทั้งหมดตามที่ระบุไว้ OP โซลูชันที่มีประสิทธิภาพน้อยกว่าคือการลบองค์ประกอบและเพิ่มองค์ประกอบนั้นใหม่ด้วยค่าที่อัปเดตแล้ว
KyleM

14
ฉันยังคงยืนยันว่าMapมีความเหมาะสมดีกว่า ( Map<Foo, Foo>ในกรณีนี้.)
dacwe

22
@dacwe ฉันมาที่นี่เพราะฉันเริ่มมองหาวิธีที่จะหลีกเลี่ยงสิ่งนั้น! วัตถุที่ทำหน้าที่ในเวลาเดียวกันทั้งที่เป็นคีย์และค่าที่สอดคล้องกันเป็นสิ่งที่ชุดควรจะเกี่ยวกับ ในกรณีของฉันฉันต้องการรับวัตถุที่ซับซ้อนบางอย่างจากชุดโดยคีย์ (String) สตริงนี้ถูกห่อหุ้ม (และไม่ซ้ำกัน) กับวัตถุที่ถูกแม็พ ในความเป็นจริงวัตถุทั้งหมด 'หมุนรอบ' ที่สำคัญกล่าวว่า นอกจากนี้ผู้เรียกรู้ว่า String แต่ไม่ใช่วัตถุ นั่นคือสาเหตุที่มันต้องการดึงข้อมูลด้วยกุญแจ แน่นอนว่าฉันใช้แผนที่ในตอนนี้ แต่มันก็ยังมีพฤติกรรมแปลก
pauluss86

4
@ KyleM ฉันเข้าใจกรณีการใช้งาน แต่ฉันต้องการเน้นถึงความสำคัญของการไม่แตะต้องคุณลักษณะที่เป็นส่วนหนึ่งของ hashCode / เท่ากับ จากชุด Javadoc: "หมายเหตุ: ต้องใช้ความระมัดระวังอย่างยิ่งหากใช้วัตถุที่ไม่แน่นอนเป็นองค์ประกอบชุดพฤติกรรมของชุดไม่ได้ระบุไว้ถ้าค่าของวัตถุมีการเปลี่ยนแปลงในลักษณะที่มีผลต่อการเปรียบเทียบเท่ากับในขณะที่วัตถุเป็น องค์ประกอบในชุด " - ฉันขอแนะนำให้วัตถุเหล่านั้นไม่เปลี่ยนรูปหรืออย่างน้อยก็มีคุณลักษณะสำคัญที่ไม่เปลี่ยนรูป
stivlo

5
ฉันยอมรับว่าคุณสามารถใช้Map<Foo, Foo>แทนข้อเสียคือแผนที่จะต้องเก็บกุญแจและค่าอย่างน้อย (และเพื่อประสิทธิภาพมันควรเก็บแฮช) ในขณะที่ชุดสามารถเก็บได้เพียงแค่เก็บค่า (และ อาจแฮสำหรับประสิทธิภาพ) ดังนั้นการติดตั้งที่ดีสามารถทำได้อย่างรวดเร็วเท่ากันMap<Foo, Foo>แต่ใช้หน่วยความจำน้อยลงมากถึง 50% ในกรณีของ Java มันจะไม่สำคัญเนื่องจาก HashSet ขึ้นอยู่กับ HashMap ภายใน
Mecki

372

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

ตอนนี้คำถามโดยนัย " วิธีการที่คุณจะได้รับธาตุแล้ว": ผมคิดว่าทางออกที่ดีที่สุดคือการใช้Map<E,E>แทนSet<E>การแมองค์ประกอบเพื่อตัวเอง ด้วยวิธีนี้คุณสามารถดึงองค์ประกอบจาก "set" ได้อย่างมีประสิทธิภาพเนื่องจากวิธี get () ของMapจะค้นหาองค์ประกอบโดยใช้ตารางแฮชที่มีประสิทธิภาพหรืออัลกอริทึมทรี หากคุณต้องการคุณสามารถเขียนการดำเนินการของตัวเองของSetข้อเสนอพิเศษที่เพิ่มเติมget()วิธีการห่อหุ้มเซลล์แสงอาทิตย์Map

คำตอบต่อไปนี้อยู่ในความเห็นของฉันไม่ดีหรือผิด:

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

"คุณไม่มีตัวเลือกอื่น ๆ แต่จะใช้ iterator ว่า" นั่นคือการค้นหาเส้นตรงในคอลเลกชันที่มีทั้งหมดไม่มีประสิทธิภาพสำหรับชุดใหญ่ (แดกดันภายในSetมีการจัดแผนที่กัญชาหรือต้นไม้ที่สามารถสอบถามได้อย่างมีประสิทธิภาพ) อย่าทำมัน! ฉันได้เห็นปัญหาประสิทธิภาพการทำงานที่รุนแรงในระบบในชีวิตจริงโดยใช้วิธีการที่ ในความคิดของฉันสิ่งที่แย่มากเกี่ยวกับget()วิธีการที่หายไปนั้นไม่ได้ยุ่งยากมากนัก แต่ก็ยากที่จะแก้ไข แต่ผู้เขียนโปรแกรมส่วนใหญ่จะใช้วิธีการค้นหาแบบเชิงเส้นโดยไม่ต้องนึกถึงผลกระทบ


27
Meh การเอาชนะการใช้งานของเท่ากับเพื่อให้วัตถุที่ไม่เท่ากันคือ "เท่ากัน" เป็นปัญหาที่นี่ การขอวิธีการที่บอกว่า "ให้ฉันเป็นวัตถุที่เหมือนกันกับวัตถุนี้" แล้วคาดว่าวัตถุที่ไม่เหมือนกันที่จะถูกส่งกลับมานั้นดูจะบ้าและง่ายที่จะทำให้เกิดปัญหาในการบำรุงรักษา อย่างที่คนอื่น ๆ แนะนำไว้การใช้แผนที่จะช่วยแก้ปัญหาเหล่านี้ทั้งหมดและทำให้สิ่งที่คุณกำลังอธิบายตนเอง มันง่ายที่จะเข้าใจว่าวัตถุที่ไม่เท่ากันสองตัวอาจมีคีย์เดียวกันในแผนที่และการมีคีย์เดียวกันจะแสดงความสัมพันธ์ระหว่างพวกเขา
David Ogren

20
คำพูดที่แข็งแกร่ง @David Ogren Meh? บ้า? แต่ในความคิดเห็นของคุณคุณกำลังใช้คำว่า "เหมือนกัน" และ "เท่าเทียมกัน" ราวกับว่าพวกเขามีความหมายเหมือนกัน พวกเขาไม่ได้. โดยเฉพาะอย่างยิ่งใน Java ข้อมูลประจำตัวจะถูกแสดงโดยโอเปอเรเตอร์ "==" และความเท่าเทียมกันแสดงด้วยเมธอด equals () หากพวกเขาหมายถึงสิ่งเดียวกันมันจะไม่จำเป็นต้องมีวิธีการเท่ากับ () เลย ในภาษาอื่น ๆ หลักสูตรนี้อาจแตกต่างกัน ตัวอย่างเช่นใน Groovy identity คือเมธอด is () และความเสมอภาคคือ "==" ตลกใช่มั้ย
jschreiner

15
การวิจารณ์ของคุณเกี่ยวกับการใช้คำว่าเหมือนกันเมื่อฉันควรใช้คำที่เทียบเท่านั้นมีความถูกต้องมาก แต่การกำหนดเท่ากับบนวัตถุเพื่อให้ Foo และ Bar นั้น "เท่ากัน" แต่ไม่เท่ากับ "เท่ากัน" สำหรับเขาที่จะใช้พวกเขาอย่างเท่าเทียมกันจะสร้างปัญหาทุกประเภททั้งกับการทำงานและการอ่าน / การบำรุงรักษา ปัญหานี้กับ Set just the iceberg สุดยอดสำหรับปัญหาที่อาจเกิดขึ้น ตัวอย่างเช่นวัตถุเท่ากันจะต้องมีรหัสแฮชเท่ากัน ดังนั้นเขาจะมีการชนกันของแฮชที่อาจเกิดขึ้น มันบ้าที่จะคัดค้านการเรียก. get (foo) โดยเฉพาะเพื่อให้ได้สิ่งอื่นที่ไม่ใช่ foo หรือไม่?
David Ogren

12
มันอาจจะน่าสังเกตว่าเช่น HashSet ถูกนำมาใช้เป็น wrapper รอบ HashMap (ที่แมปกุญแจกับค่าหุ่น) ดังนั้นการใช้ HashMap อย่างชัดเจนแทน HashSet จะไม่ทำให้เกิดค่าใช้จ่ายในการใช้หน่วยความจำ
Alexey B.

4
@ user686249 ฉันรู้สึกว่าสิ่งนี้ได้กลายเป็นเพียงการอภิปรายทางวิชาการ ฉันยอมรับว่าฉันอาจลงน้ำในการคัดค้านการเอาชนะเท่ากับ โดยเฉพาะอย่างยิ่งในการใช้งานเช่นคุณ แต่ฉันยังคงมีความคิดที่จะเรียกวิธีget()นี้ ในตัวอย่างของคุณฉันจะสับสนมากโดย customerSet.get (thisCustomer) (ในขณะที่แผนที่ตามคำแนะนำหลายคำตอบ) จะดีกับ canonicalCustomerMap.get (ลูกค้ารายนี้) ฉันก็จะตกลงกับวิธีการที่มีชื่อชัดเจนมากขึ้น (เช่นวิธีสมาชิกของ Objective-C บน NSSet)
David Ogren

19

หากคุณมีวัตถุที่เท่ากันทำไมคุณถึงต้องการวัตถุนั้นจากชุด? หากเป็น "เท่ากัน" โดยคีย์เท่านั้นMapจะเป็นทางเลือกที่ดีกว่า

อย่างไรก็ตามสิ่งต่อไปนี้จะทำ:

Foo getEqual(Foo sample, Set<Foo> all) {
  for (Foo one : all) {
    if (one.equals(sample)) {
      return one;
    }
  } 
  return null;
}

ด้วย Java 8 สิ่งนี้สามารถกลายเป็นสายการบินเดียว:

return all.stream().filter(sample::equals).findAny().orElse(null);

ฉันชอบคำตอบนี้ดีกว่าฉันจะหลีกเลี่ยงการใช้คำสั่ง return สองรายการเนื่องจากมันขัดกับ OOP และทำให้ค่า Cyclomatic Complexity สูงขึ้น
ลีโอ

8
@Leo ขอบคุณ แต่กระบวนทัศน์ทางออกเดียวไม่ได้ต่อต้าน OOP และส่วนใหญ่ไม่ถูกต้องสำหรับภาษาที่ทันสมัยกว่า Fortran หรือ COBOL โปรดดูsoftwareengineering.stackexchange.com/questions/118703/ …
Arne Burmeister

1
การใช้แผนที่แทนที่จะเป็นชุดนั้นดูเหมือนจะเป็นตัวเลือกที่ดีกว่าการวนซ้ำองค์ประกอบต่างๆของชุดจะทำงานได้ดีกว่าการรับค่าเดียวจากแผนที่ (O (N) vs O (1))
Jamie Flournoy

@JamieFlournoy ถูกต้องถ้าคุณต้องตรวจสอบชุดเดียวกันสำหรับองค์ประกอบต่าง ๆ หลายครั้งดีกว่ามาก สำหรับการใช้งานครั้งเดียวไม่ได้เนื่องจากต้องใช้ความพยายามมากขึ้นในการสร้างแผนที่ก่อน
Arne Burmeister

18

แปลงชุดเป็นรายการแล้วใช้getวิธีรายการ

Set<Foo> set = ...;
List<Foo> list = new ArrayList<Foo>(set);
Foo obj = list.get(0);

37
ฉันไม่ได้รับสิ่งนี้ นี้จะดึงพลวัตถุของชุด ไม่วัตถุ
aioobe

14

ตั้งค่าเริ่มต้นใน Java เป็นโชคไม่ดีที่ไม่ได้ออกแบบมาเพื่อให้การดำเนินงาน "รับ" ตามที่jschreinerอธิบายอย่างถูกต้อง

วิธีแก้ปัญหาของการใช้ตัววนซ้ำเพื่อค้นหาองค์ประกอบที่น่าสนใจ (แนะนำโดยdacwe ) หรือลบองค์ประกอบแล้วเพิ่มเข้าไปใหม่ด้วยค่าที่อัปเดต (แนะนำโดยKyleM ) สามารถใช้งานได้ แต่ไม่มีประสิทธิภาพมาก

การเอาชนะการใช้งานของเท่ากับเพื่อให้วัตถุที่ไม่เท่ากันคือ "เท่ากัน" ตามที่ระบุไว้อย่างถูกต้องโดยDavid Ogrenสามารถทำให้เกิดปัญหาการบำรุงรักษาได้อย่างง่ายดาย

และการใช้แผนที่เป็นสิ่งทดแทนอย่างชัดเจน (ตามคำแนะนำของหลาย ๆ คน) ทำให้โค้ดนั้นดูหรูหราน้อยลง

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


ฉันเองมีความต้องการเดียวกันกับคุณในขณะที่พัฒนาวิดีโอเกมไคลเอนต์เซิร์ฟเวอร์ด้วย Java ในกรณีของฉันลูกค้าแต่ละรายมีสำเนาของส่วนประกอบที่เก็บไว้ในเซิร์ฟเวอร์และปัญหาคือเมื่อใดก็ตามที่ลูกค้าต้องการแก้ไขวัตถุของเซิร์ฟเวอร์

การส่งวัตถุผ่านอินเทอร์เน็ตหมายความว่าลูกค้ามีอินสแตนซ์ต่าง ๆ ของวัตถุนั้นอยู่ดี เพื่อให้ตรงกับอินสแตนซ์ "คัดลอก" นี้กับอินสแตนซ์ดั้งเดิมฉันตัดสินใจใช้ Java UUID

ดังนั้นฉันจึงสร้างคลาสนามธรรม UniqueItem ซึ่งจะให้รหัสเฉพาะที่ไม่ซ้ำแบบสุ่มให้กับแต่ละอินสแตนซ์ของคลาสย่อย

UUID นี้ใช้ร่วมกันระหว่างไคลเอนต์และอินสแตนซ์ของเซิร์ฟเวอร์ดังนั้นวิธีนี้จึงสามารถจับคู่ได้ง่ายโดยใช้แผนที่

อย่างไรก็ตามการใช้แผนที่ใน usecase ที่คล้ายกันโดยตรงนั้นยังไม่เหมาะสม บางคนอาจโต้แย้งว่าการใช้แผนที่อาจมีความซับซ้อนมากขึ้นในการจัดการและจัดการ

ด้วยเหตุผลเหล่านี้ฉันจึงติดตั้งไลบรารีชื่อ MagicSet ซึ่งทำให้การใช้แผนที่ "โปร่งใส" แก่ผู้พัฒนา

https://github.com/ricpacca/magicset


เช่นเดียวกับ Java HashSet ดั้งเดิม MagicHashSet (ซึ่งเป็นหนึ่งในการนำไปใช้งานของ MagicSet ที่ให้ไว้ในไลบรารี) ใช้ HashMap สำรอง แต่แทนที่จะมีองค์ประกอบเป็นคีย์และค่าดัมมี่เป็นค่าใช้ UUID ขององค์ประกอบเป็นคีย์ และองค์ประกอบเองเป็นค่า นี่ไม่ทำให้เกิดโอเวอร์เฮดในการใช้หน่วยความจำเมื่อเปรียบเทียบกับ HashSet ปกติ

ยิ่งไปกว่านั้น MagicSet สามารถใช้งานได้เหมือน Set แต่ด้วยวิธีการบางอย่างที่ให้ฟังก์ชันการทำงานเพิ่มเติมเช่น getFromId (), popFromId (), removeFromId () เป็นต้น

ข้อกำหนดเพียงอย่างเดียวที่จะใช้คือองค์ประกอบใด ๆ ที่คุณต้องการเก็บไว้ใน MagicSet จำเป็นต้องขยายคลาสนามธรรม UniqueItem


นี่คือตัวอย่างของรหัสลองจินตนาการถึงการดึงอินสแตนซ์ดั้งเดิมของเมืองจาก MagicSet ที่กำหนดอินสแตนซ์อื่นของเมืองนั้นด้วย UUID เดียวกัน (หรือแม้แต่แค่ UUID)

class City extends UniqueItem {

    // Somewhere in this class

    public void doSomething() {
        // Whatever
    }
}

public class GameMap {
    private MagicSet<City> cities;

    public GameMap(Collection<City> cities) {
        cities = new MagicHashSet<>(cities);
    }

    /*
     * cityId is the UUID of the city you want to retrieve.
     * If you have a copied instance of that city, you can simply 
     * call copiedCity.getId() and pass the return value to this method.
     */
    public void doSomethingInCity(UUID cityId) {
        City city = cities.getFromId(cityId);
        city.doSomething();
    }

    // Other methods can be called on a MagicSet too
}

11

หากชุดของคุณเป็นจริงแล้วNavigableSet<Foo>(เช่น a TreeSet) และFoo implements Comparable<Foo>คุณสามารถใช้

Foo bar = set.floor(foo); // or .ceiling
if (foo.equals(bar)) {
    // use bar…
}

(ขอบคุณที่ความคิดเห็นของ @ eliran-malka สำหรับคำใบ้)


5
ถ้าฉันไม่คิดว่าใครที่อ่านโค้ดของฉันโดยมีความคิดเริ่มต้นว่าฉันเสียสติไปแล้วนี่จะเป็นทางออกที่ดี
อดัม

10

ด้วย Java 8 คุณสามารถทำได้:

Foo foo = set.stream().filter(item->item.equals(theItemYouAreLookingFor)).findFirst().get();

แต่ระวัง,. get () ขว้าง NoSuchElementException หรือคุณสามารถจัดการไอเท็มเสริมได้


5
item->item.equals(theItemYouAreLookingFor)สามารถย่อให้เล็กลงได้theItemYouAreLookingFor::equals
Henno Vermeulen

5
Object objectToGet = ...
Map<Object, Object> map = new HashMap<Object, Object>(set.size());
for (Object o : set) {
    map.put(o, o);
}
Object objectFromSet = map.get(objectToGet);

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


5

ทำไม:

ดูเหมือนว่าชุดมีบทบาทที่มีประโยชน์ในการจัดให้มีวิธีการเปรียบเทียบ มันถูกออกแบบมาไม่ให้เก็บองค์ประกอบที่ซ้ำกัน

เนื่องจากความตั้งใจ / การออกแบบนี้หากมีใครที่จะได้รับ () อ้างอิงถึงวัตถุที่เก็บไว้จากนั้นกลายพันธุ์มันเป็นไปได้ที่ความตั้งใจในการออกแบบของ Set อาจถูกขัดขวางและอาจทำให้เกิดพฤติกรรมที่ไม่คาดคิด

จากJavaDocs

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

วิธี:

ตอนนี้สตรีมได้รับการแนะนำให้รู้จัก

mySet.stream()
.filter(object -> object.property.equals(myProperty))
.findFirst().get();

2

แล้วการใช้คลาส Arays ล่ะ?

import java.util.Arrays;
import java.util.List;
import java.util.HashSet;
import java.util.Arrays;

public class MyClass {
    public static void main(String args[]) {
        Set mySet = new HashSet();
        mySet.add("one");
        mySet.add("two");
        List list = Arrays.asList(mySet.toArray());
        Object o0 = list.get(0);
        Object o1 = list.get(1);
        System.out.println("items " + o0+","+o1);
    }
}

เอาท์พุท:
รายการหนึ่งสอง



1

ฉันรู้ว่าสิ่งนี้ถูกถามและตอบนานแล้ว แต่ถ้าใครสนใจนี่คือทางออกของฉัน - ชุดแบบกำหนดเองที่สนับสนุนโดย HashMap:

http://pastebin.com/Qv6S91n9

คุณสามารถใช้วิธีการตั้งค่าอื่น ๆ ทั้งหมดได้อย่างง่ายดาย


7
เราขอแนะนำให้ใส่ตัวอย่างแทนที่จะเชื่อมโยงกับสิ่งใดสิ่งหนึ่ง
คนงานทุกคนสำคัญเมื่อ

1

เคยทำแบบนั้นมาก่อน !! หากคุณใช้ Guava วิธีที่รวดเร็วในการแปลงเป็นแผนที่คือ:

Map<Integer,Foo> map = Maps.uniqueIndex(fooSet, Foo::getKey);

1

คุณสามารถใช้คลาส Iterator

import java.util.Iterator;
import java.util.HashSet;

public class MyClass {
 public static void main(String[ ] args) {
 HashSet<String> animals = new HashSet<String>();
animals.add("fox");
animals.add("cat");
animals.add("dog");
animals.add("rabbit");

Iterator<String> it = animals.iterator();
while(it.hasNext()) {
  String value = it.next();
  System.out.println(value);   
 }
 }
}

1

หากคุณต้องการองค์ประกอบที่ n จาก HashSet คุณสามารถไปด้วยวิธีการแก้ปัญหาด้านล่างที่นี่ฉันได้เพิ่มวัตถุของ ModelClass ใน HashSet

ModelClass m1 = null;
int nth=scanner.nextInt();
for(int index=0;index<hashset1.size();index++){
    m1 = (ModelClass) itr.next();
    if(nth == index) {
        System.out.println(m1);
        break;
    }
}

1

หากคุณดูสองสามบรรทัดแรกของการนำไปใช้java.util.HashSetคุณจะเห็น:

public class HashSet<E>
    ....
    private transient HashMap<E,Object> map;

ดังนั้นHashSetใช้HashMapinterally ซึ่งหมายความว่าถ้าคุณเพียงแค่ใช้HashMapโดยตรงและใช้ค่าเดียวกับคีย์และค่าที่คุณจะได้รับผลกระทบที่คุณต้องการและบันทึกหน่วยความจำบางอย่างด้วยตัวคุณเอง


1

ดูเหมือนว่าวัตถุที่เหมาะสมที่จะใช้คือInternerจากฝรั่ง:

จัดให้มีพฤติกรรมที่เทียบเท่ากับ String.intern () สำหรับประเภทที่ไม่เปลี่ยนรูปอื่น ๆ การใช้งานทั่วไปมีอยู่ใน คลาสInterners

มันมีคันโยกที่น่าสนใจไม่กี่อย่างเช่น concurrencyLevel หรือประเภทของการอ้างอิงที่ใช้ (อาจจะคุ้มค่าที่จะสังเกตว่ามันไม่ได้ให้ SoftInterner ซึ่งฉันเห็นว่ามีประโยชน์มากกว่า WeakInterner)


0

เพราะการดำเนินการใด ๆ โดยเฉพาะอย่างยิ่งของชุดอาจหรือไม่อาจจะเข้าถึงโดยสุ่ม

คุณสามารถรับตัววนซ้ำและก้าวผ่านเซตโดยใช้วิธีตัววนซ้ำnext()เพื่อส่งคืนผลลัพธ์ที่คุณต้องการเมื่อคุณพบองค์ประกอบที่เท่ากัน สิ่งนี้ทำงานได้โดยไม่คำนึงถึงการใช้งาน หากการใช้งานไม่ใช่การเข้าถึงแบบสุ่ม (รูปภาพเชื่อมโยงชุดรายการสำรอง) get(E element)วิธีการในอินเทอร์เฟซจะหลอกลวงเนื่องจากจะต้องย้ำคอลเลกชันเพื่อหาองค์ประกอบที่จะกลับมาและget(E element)ดูเหมือนจะหมายความว่านี่จะ จำเป็นที่ Set สามารถข้ามไปยังองค์ประกอบได้โดยตรง

contains() อาจหรืออาจไม่ต้องทำสิ่งเดียวกันแน่นอนขึ้นอยู่กับการใช้งาน แต่ชื่อไม่ดูเหมือนจะยืมตัวเองไปยังประเภทของความเข้าใจผิดที่เหมือนกัน


2
ทุกสิ่งที่เมธอด get () จะทำเสร็จสิ้นแล้วโดยเมธอด contain () คุณไม่สามารถใช้ contain () โดยไม่ได้รับออบเจ็กต์ที่มีอยู่และเรียกใช้เมธอด. equals () ผู้ออกแบบ API ดูเหมือนจะไม่มีความมั่นใจในการเพิ่ม get () ไปยัง java.util.List แม้ว่ามันจะช้าในการใช้งานบางอย่าง
ไบรอันริงค์

ฉันไม่คิดว่านี่เป็นเรื่องจริง วัตถุสองชิ้นสามารถเท่ากันผ่านทางเท่ากับ แต่ไม่เหมือนกันผ่าน == หากคุณมีวัตถุ A และชุด S ที่มีวัตถุ B และ A.equals (B) แต่ A! = B และคุณต้องการได้รับการอ้างอิงถึง B คุณสามารถโทรหา S.get (A) เพื่อรับการอ้างอิงถึง B สมมติว่าคุณมีวิธีรับด้วยวิธีการรับความหมายของรายการซึ่งเป็นกรณีการใช้งานที่แตกต่างจากการตรวจสอบว่า S.contain (A) (ซึ่งจะ) นี่ไม่ใช่กรณีที่ใช้สำหรับคอลเลกชันที่หายาก
Tom Tresansky

0

ใช่ใช้HashMap... แต่ด้วยวิธีพิเศษ: กับดักที่ฉันคาดหวังในการพยายามใช้HashMapเป็นหลอก - Setเป็นความสับสนที่เป็นไปได้ระหว่างองค์ประกอบ "ที่แท้จริง" ขององค์ประกอบMap/Setและ "ผู้สมัคร" องค์ประกอบคือองค์ประกอบที่ใช้ในการทดสอบว่าequalองค์ประกอบมีอยู่แล้ว สิ่งนี้อยู่ไกลจากความเข้าใจผิด แต่ทำให้คุณห่างจากกับดัก:

class SelfMappingHashMap<V> extends HashMap<V, V>{
    @Override
    public String toString(){
        // otherwise you get lots of "... object1=object1, object2=object2..." stuff
        return keySet().toString();
    }

    @Override
    public V get( Object key ){
        throw new UnsupportedOperationException( "use tryToGetRealFromCandidate()");
    }

    @Override
    public V put( V key, V value ){
       // thorny issue here: if you were indavertently to `put`
       // a "candidate instance" with the element already in the `Map/Set`: 
       // these will obviously be considered equivalent 
       assert key.equals( value );
       return super.put( key, value );
    }

    public V tryToGetRealFromCandidate( V key ){
        return super.get(key);
    }
}

จากนั้นทำสิ่งนี้:

SelfMappingHashMap<SomeClass> selfMap = new SelfMappingHashMap<SomeClass>();
...
SomeClass candidate = new SomeClass();
if( selfMap.contains( candidate ) ){
    SomeClass realThing = selfMap.tryToGetRealFromCandidate( candidate );
    ...
    realThing.useInSomeWay()...
}

แต่ ... ตอนนี้คุณต้องการcandidateทำลายตัวเองในทางใดทางหนึ่งเว้นแต่ว่าโปรแกรมเมอร์จะใส่มันลงไปในทันทีMap/Set... คุณต้องการcontains"ทำให้มัวหมอง" candidateเพื่อที่ว่าการใช้งานใด ๆ ของมันเว้นแต่ว่ามันจะเข้าร่วมMapทำให้ "คำสาปแช่ง " บางทีคุณสามารถสร้างอินเทอร์เฟซSomeClassใหม่Taintableได้

โซลูชันที่น่าพอใจมากขึ้นคือGettableSetดังต่อไปนี้ อย่างไรก็ตามในการทำงานคุณต้องรับผิดชอบในการออกแบบSomeClassเพื่อที่จะทำให้คอนสตรัคเตอร์ทั้งหมดไม่สามารถมองเห็นได้ (หรือ ... สามารถและเต็มใจที่จะออกแบบและใช้คลาส wrapper สำหรับมัน):

public interface NoVisibleConstructor {
    // again, this is a "nudge" technique, in the sense that there is no known method of 
    // making an interface enforce "no visible constructor" in its implementing classes 
    // - of course when Java finally implements full multiple inheritance some reflection 
    // technique might be used...
    NoVisibleConstructor addOrGetExisting( GettableSet<? extends NoVisibleConstructor> gettableSet );
};

public interface GettableSet<V extends NoVisibleConstructor> extends Set<V> {
    V getGenuineFromImpostor( V impostor ); // see below for naming
}

การดำเนินงาน:

public class GettableHashSet<V extends NoVisibleConstructor> implements GettableSet<V> {
    private Map<V, V> map = new HashMap<V, V>();

    @Override
    public V getGenuineFromImpostor(V impostor ) {
        return map.get( impostor );
    }

    @Override
    public int size() {
        return map.size();
    }

    @Override
    public boolean contains(Object o) {
        return map.containsKey( o );
    }

    @Override
    public boolean add(V e) {
        assert e != null;
        V result = map.put( e,  e );
        return result != null;
    }

    @Override
    public boolean remove(Object o) {
        V result = map.remove( o );
        return result != null;
    }

    @Override
    public boolean addAll(Collection<? extends V> c) {
        // for example:
        throw new UnsupportedOperationException();
    }

    @Override
    public void clear() {
        map.clear();
    }

    // implement the other methods from Set ...
}

NoVisibleConstructorชั้นเรียนของคุณมีลักษณะดังนี้:

class SomeClass implements NoVisibleConstructor {

    private SomeClass( Object param1, Object param2 ){
        // ...
    }

    static SomeClass getOrCreate( GettableSet<SomeClass> gettableSet, Object param1, Object param2 ) {
        SomeClass candidate = new SomeClass( param1, param2 );
        if (gettableSet.contains(candidate)) {
            // obviously this then means that the candidate "fails" (or is revealed
            // to be an "impostor" if you will).  Return the existing element:
            return gettableSet.getGenuineFromImpostor(candidate);
        }
        gettableSet.add( candidate );
        return candidate;
    }

    @Override
    public NoVisibleConstructor addOrGetExisting( GettableSet<? extends NoVisibleConstructor> gettableSet ){
       // more elegant implementation-hiding: see below
    }
}

ปัญหาด้านเทคนิคของ PS หนึ่งที่มีNoVisibleConstructorคลาสดังกล่าว: อาจเป็นที่โต้แย้งได้ว่าคลาสนั้นมีอยู่โดยเนื้อแท้finalซึ่งอาจไม่พึงประสงค์ ที่จริงแล้วคุณสามารถเพิ่มตัวprotectedสร้างพารามิเตอร์ที่ไม่ใช้พารามิเตอร์จำลอง:

protected SomeClass(){
    throw new UnsupportedOperationException();
}

... ซึ่งอย่างน้อยจะให้คอมไพล์คลาสย่อย จากนั้นคุณต้องคิดว่าคุณจะต้องรวมgetOrCreate()วิธีการโรงงานอื่นในคลาสย่อยหรือไม่

ขั้นตอนสุดท้ายคือระดับฐานนามธรรม (องค์ประกอบ "NB" สำหรับรายการ "สมาชิก" สำหรับชุด) เช่นนี้สำหรับสมาชิกชุดของคุณ (เมื่อเป็นไปได้ - อีกครั้งขอบเขตสำหรับการใช้คลาส wrapperที่ชั้นไม่ได้อยู่ภายใต้การควบคุมของคุณ หรือมีคลาสพื้นฐานแล้ว) สำหรับการซ่อนการใช้งานสูงสุด:

public abstract class AbstractSetMember implements NoVisibleConstructor {
    @Override
    public NoVisibleConstructor
            addOrGetExisting(GettableSet<? extends NoVisibleConstructor> gettableSet) {
        AbstractSetMember member = this;
        @SuppressWarnings("unchecked") // unavoidable!
        GettableSet<AbstractSetMembers> set = (GettableSet<AbstractSetMember>) gettableSet;
        if (gettableSet.contains( member )) {
            member = set.getGenuineFromImpostor( member );
            cleanUpAfterFindingGenuine( set );
        } else {
            addNewToSet( set );
        }
        return member;
    }

    abstract public void addNewToSet(GettableSet<? extends AbstractSetMember> gettableSet );
    abstract public void cleanUpAfterFindingGenuine(GettableSet<? extends AbstractSetMember> gettableSet );
}

... การใช้งานเป็นที่ชัดเจนเป็นธรรม (ภายในของคุณSomeClass's staticวิธีโรงงาน):

SomeClass setMember = new SomeClass( param1, param2 ).addOrGetExisting( set );

0

สัญญาของรหัสแฮชทำให้ชัดเจนว่า:

"หากวัตถุสองชิ้นมีค่าเท่ากันตามวิธีการของวัตถุการเรียกใช้วิธี hashCode บนวัตถุทั้งสองนั้นจะต้องให้ผลลัพธ์ที่เป็นจำนวนเต็มเหมือนกัน"

ดังนั้นสมมติฐานของคุณ:

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

ผิดและคุณผิดสัญญา หากเราดูที่ "มี" วิธีการตั้งค่าอินเตอร์เฟซเรามี:

บูลีนมี (วัตถุ o);
ผลตอบแทนจริงถ้าชุดนี้มีองค์ประกอบที่ระบุ เป็นทางการมากขึ้นส่งคืนจริงถ้าหากเซตนี้มีองค์ประกอบ "e" เช่นนั้น == null? e == null: o.equals (e)

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


-2

วิธีช่วยเหลือด่วนที่อาจแก้ไขสถานการณ์นี้:

<T> T onlyItem(Collection<T> items) {
    if (items.size() != 1)
        throw new IllegalArgumentException("Collection must have single item; instead it has " + items.size());

    return items.iterator().next();
}

8
มันแปลกมากที่คำตอบนี้ได้รับ upvotes มากมายเพราะมันไม่ได้ตอบคำถามหรือแม้แต่พยายามที่จะตอบมันในทางใดทางหนึ่ง
David Conrad

-2

ต่อไปนี้เป็นวิธีการ

   SharedPreferences se_get = getSharedPreferences("points",MODE_PRIVATE);
   Set<String> main = se_get.getStringSet("mydata",null);
   for(int jk = 0 ; jk < main.size();jk++)
   {
      Log.i("data",String.valueOf(main.toArray()[jk]));
   }

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