แปลงชุดเป็นรายการโดยไม่ต้องสร้างรายการใหม่


503

ฉันใช้รหัสนี้เพื่อแปลง a Setเป็นList:

Map<String, List<String>> mainMap = new HashMap<>();

for (int i=0; i < something.size(); i++) {
  Set<String> set = getSet(...); //returns different result each time
  List<String> listOfNames = new ArrayList<>(set);
  mainMap.put(differentKeyName, listOfNames);
}

ฉันต้องการหลีกเลี่ยงการสร้างรายการใหม่ในการวนซ้ำแต่ละครั้ง เป็นไปได้ไหม


1
ฉันรู้วิธีการแปลงชุดเป็นรายการเช่นเดียวกับใน Q ฉันต้องการหลีกเลี่ยงการสร้างรายการใหม่ทุกครั้งในวง
มูฮัมหมัดอิมรันทาเร็ค

4
ทำไมคุณไม่สามารถเพิ่มชุดไปยัง mainList ได้? ทำไมคุณต้องแปลงชุดเป็นรายการ?
DagR

1
เป็นความตั้งใจของคุณหรือไม่ที่จะสร้างรายการ <List <? >>
Hiery Nomus

5
คุณทำไม่ได้ คำถามของคุณสะท้อนถึงความขัดแย้งในแง่
มาร์ควิสแห่ง Lorne

คำตอบ:


802

คุณสามารถใช้List.addAll ()วิธีการ ยอมรับการรวบรวมเป็นอาร์กิวเมนต์และชุดของคุณคือการรวบรวม

List<String> mainList = new ArrayList<String>();
mainList.addAll(set);

แก้ไข:ตอบสนองต่อการแก้ไขของคำถาม
มันเป็นเรื่องง่ายที่จะเห็นว่าถ้าคุณต้องการที่จะมีMapด้วยListในฐานะที่เป็นค่านิยมเพื่อให้มีค่าที่แตกต่าง k, คุณจำเป็นต้องสร้าง k รายการที่แตกต่างกัน
ดังนั้น: คุณไม่สามารถหลีกเลี่ยงการสร้างรายการเหล่านี้ได้ทุกรายการจะต้องถูกสร้างขึ้น

การทำงานที่เป็นไปได้:
ประกาศของคุณMapเป็น a Map<String,Set>หรือMap<String,Collection>แทนและเพียงแค่ใส่ชุดของคุณ


1
ขออภัยเป็น mainMap ไม่แสดงรายการ ดูคำถาม
Muhammad Imran Tariq

@imrantariq: มีdifferentKeyNameการเปลี่ยนแปลงซ้ำทุกครั้งหรือไม่ คุณต้องการsomething.size()ค่าที่เป็นไปได้ที่แตกต่างกันในแผนที่ของคุณหรือไม่? มันง่ายที่จะเห็นว่าแผนที่ที่มีkรายชื่อเป็นค่าจำเป็นต้องสร้างอย่างน้อยkรายการ
amit

@imrantariq: และคุณต้องการรายการที่แตกต่างกันสำหรับแต่ละคีย์ฉันถือว่า?
Amit

@imrantariq: สิ่งที่คุณขอเป็นไปไม่ได้ อ่านการแก้ไขของฉันสำหรับรายละเอียดเพิ่มเติม
amit

มันจะส่งคืน NullPointerException ในกรณีที่ชุดเป็นโมฆะ
w35l3y

411

ใช้ Constructor เพื่อแปลง:

List<?> list = new ArrayList<?>(set);

21
เขาบอกว่าเขาต้องการหลีกเลี่ยงสิ่งนี้โดยเฉพาะ
mapeters

3
@ มุกไม่เกี่ยวข้องเนื่องจากความต้องการของเขาไม่สามารถใช้งานได้
มาร์ควิสแห่ง Lorne

16
@EJP แล้วคำตอบของเขาต้องบอกว่าแทนที่จะเพียงแค่บอกอะไรบางอย่างที่ OP ไม่ได้ขอโดยไม่มีคำอธิบายใด ๆ
mapeters

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

สิ่งนี้ใช้ไม่ได้กับ Android เหตุผลใด
kbluue

84

นอกจากนี้จากห้องสมุดรวบรวม Guava คุณสามารถใช้newArrayList(Collection):

Lists.newArrayList([your_set])

นี้จะคล้ายกับคำตอบก่อนหน้านี้จากamitยกเว้นว่าคุณไม่จำเป็นต้องประกาศ (หรือ instanciate) listวัตถุใด ๆ


1
หากคุณใช้ฝรั่งนี่เป็นประโยชน์อย่างมาก
vsingh

6
แม้ว่าคุณจะไม่ได้เรียกตัวสร้างโดยตรงวิธีนี้ยังคงเรียกตัวArrayListสร้าง
glen3b

หากฉันไม่ประกาศรายการฉันจะใช้รายการที่สร้างได้อย่างไร
Koray Tugay

@KorayTugay เป็นอย่างดีที่คุณแยกLists.newArrayList ([your_set])ในตัวแปร (ท้องถิ่นหรือทั่วโลก) ตัวอย่างเช่น: รายการ <Foo> fooList = Lists.newArrayList (setOfFoo) แต่คำถามของคุณมีข้อบกพร่อง หากคุณสร้างรายการอย่างน้อยที่สุดก็จะมีการประกาศโดยนัย (ถ้าไม่ใช่อย่างชัดเจน)
chaiyachaiya

1
มีการเดาว่าทำไมจึงทำวิธีนี้ ดูเหมือนจะไม่ดีไปกว่าnew ArrayList<>([your_set])นี้อีกแล้ว
DavidS

49

เราสามารถใช้หนึ่งซับในต่อไปนี้ใน Java 8:

List<String> list = set.stream().collect(Collectors.toList());

นี่คือตัวอย่างเล็ก ๆ น้อย ๆ :

public static void main(String[] args) {
        Set<String> set = new TreeSet<>();
        set.add("A");
        set.add("B");
        set.add("C");
        List<String> list = set.stream().collect(Collectors.toList());
}

7
เพื่อความสะดวกในการอ่านไม่แนะนำ ตัวอย่างเช่น IntelliJ แนะนำ "ใหม่ ArrayList <> (set)" และแสดงตัวอย่างโค้ดที่คล้ายกันมากกว่า 20 ตัวอย่างด้วยสามารถถูกแทนที่ด้วยวิธีเดียวกัน
rrhrg

ตรง! @rrgg ซึ่งจะดีกว่าสำหรับประสิทธิภาพถ้าเราใช้ set.parallelStream ()?
gaurav

31

ทางออกที่ง่ายที่สุด

ฉันต้องการวิธีที่รวดเร็วมากในการแปลงชุดของฉันเป็นรายการและส่งคืนดังนั้นในหนึ่งบรรทัดที่ฉันทำ

 return new ArrayList<Long>(mySetVariable);

1
นี่คือสิ่งที่ IntelliJ IDEA แนะนำแทนที่จะเป็นสตรีม API
Ben

6

คุณสามารถใช้การเปลี่ยนแปลงหนึ่งบรรทัดนี้: Arrays.asList(set.toArray(new Object[set.size()]))

Map<String, List> mainMap = new HashMap<String, List>();

for(int i=0; i<something.size(); i++){
  Set set = getSet(...); 
  mainMap.put(differentKeyName, Arrays.asList(set.toArray(new Object[set.size()])));
}  

ขนาดที่ถูกต้องเนื่องจากวัตถุใหม่ [0] จะเก็บเพียงหนึ่งองค์ประกอบ แต่วัตถุใหม่ [set.size ()] จะเก็บค่าทั้งหมด
rajadilipkolli


5

เนื่องจากยังไม่ได้รับการกล่าวถึงจนถึงขณะนี้ใน Java 10 คุณสามารถใช้วิธีการใหม่จากcopyOfโรงงาน:

List.copyOf(set);

จากJavadoc :

ส่งคืนรายการที่ไม่สามารถเปลี่ยนแปลงได้ที่มีองค์ประกอบของคอลเล็กชันที่กำหนดตามลำดับการวนซ้ำ


3

Java 8 มีตัวเลือกในการใช้สตรีมและคุณสามารถรับรายการจากSet<String> setString:

List<String> stringList = setString.stream().collect(Collectors.toList());

แม้ว่าการใช้งานภายใน ณ ตอนนี้จะเป็นตัวอย่างของArrayList:

public static <T>
    Collector<T, ?, List<T>> toList() {
        return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
                                   (left, right) -> { left.addAll(right); return left; },
                                   CH_ID);
    }

แต่ JDK ไม่รับประกัน ดังที่กล่าวไว้ที่นี่ :

ไม่มีการรับประกันเกี่ยวกับประเภทความไม่แน่นอนความต่อเนื่องของอนุกรมหรือความปลอดภัยของเธรดของรายการที่ส่งคืน หากต้องการการควบคุมมากกว่ารายการที่ส่งคืนให้ใช้ toCollection (ซัพพลายเออร์)

ในกรณีที่คุณต้องการให้แน่ใจเสมอคุณสามารถขออินสแตนซ์ได้โดยเฉพาะดังนี้:

List<String> stringArrayList = setString.stream()
                     .collect(Collectors.toCollection(ArrayList::new));

2

ฉันสร้างstaticวิธีการง่าย ๆ:

public static <U> List<U> convertSetToList(Set<U> set)
{
    return new ArrayList<U>(set);
}

... หรือถ้าคุณต้องการตั้งค่าชนิดของรายการคุณสามารถใช้:

public static <U, L extends List<U>> List<U> convertSetToList(Set<U> set, Class<L> clazz) throws InstantiationException, IllegalAccessException
{
    L list = clazz.newInstance();
    list.addAll(set);
    return list;
}

2

เมื่อเร็ว ๆ นี้ฉันพบสิ่งนี้:

ArrayList<T> yourList = Collections.list(Collections.enumeration(yourSet<T>));

1
คุณสามารถขยายหรือขยายเพิ่มเติมเกี่ยวกับเรื่องนี้ได้หรือไม่?
Vandal

Collections.list () สร้าง ArrayList ใหม่:public static <T> ArrayList<T> list(Enumeration<T> e) { ArrayList<T> l = new ArrayList<>(); while (e.hasMoreElements()) l.add(e.nextElement()); return l; }
Artem Lukanin

2

เพื่อความสมบูรณ์ ...

บอกว่าคุณจริงๆไม่ต้องการที่จะรักษาMapค่าเป็นLists แต่คุณต้องการที่จะหลีกเลี่ยงการคัดลอกSetลงในListแต่ละครั้ง

ยกตัวอย่างเช่นบางทีคุณอาจจะเรียกฟังก์ชันหนึ่งที่สร้างSetแต่คุณจะผ่านของคุณMap<String, List<String>>ผลไป (ไม่ดีการออกแบบ แต่ออกมาจากมือของคุณ) ฟังก์ชั่นห้องสมุดที่ใช้เวลาเพียงMap<String, List<String>>แม้อย่างใดที่คุณจะรู้ว่าการดำเนินการมันไม่ด้วยLists เท่าเทียมกันบังคับใด ๆCollection(เช่นใด ๆSet) และด้วยเหตุผลบางอย่างคุณจำเป็นต้องหลีกเลี่ยงค่าใช้จ่ายความเร็ว / หน่วยความจำในการคัดลอกแต่ละชุดเป็นรายการ

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

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

โปรดทราบว่าพฤติกรรมบางอย่างของรายการคุณจะไม่สามารถเลียนแบบได้โดยไม่ต้องเก็บองค์ประกอบไว้เป็น a Listและพฤติกรรมบางอย่างที่คุณจะเลียนแบบได้เพียงบางส่วนเท่านั้น อีกครั้งคลาสนี้ไม่ใช่การแทนที่ดร็อปดาวน์อย่างปลอดภัยสำหรับLists โดยทั่วไป โดยเฉพาะอย่างยิ่งถ้าคุณรู้ว่ากรณีการใช้งานต้องการการดำเนินการที่เกี่ยวข้องกับดัชนีหรือการตัดทอนListวิธีนี้จะไปทางทิศใต้อย่างรวดเร็ว

public class ListViewOfSet<U> implements List<U> {
    private final Set<U> wrappedSet;
    public ListViewOfSet(Set<U> setToWrap) { this.wrappedSet = setToWrap; }

    @Override public int size() { return this.wrappedSet.size(); }
    @Override public boolean isEmpty() { return this.wrappedSet.isEmpty(); }
    @Override public boolean contains(Object o) { return this.wrappedSet.contains(o); }
    @Override public java.util.Iterator<U> iterator() { return this.wrappedSet.iterator(); }
    @Override public Object[] toArray() { return this.wrappedSet.toArray(); }
    @Override public <T> T[] toArray(T[] ts) { return this.wrappedSet.toArray(ts); }
    @Override public boolean add(U e) { return this.wrappedSet.add(e); }
    @Override public boolean remove(Object o) { return this.wrappedSet.remove(o); }
    @Override public boolean containsAll(Collection<?> clctn) { return this.wrappedSet.containsAll(clctn); }
    @Override public boolean addAll(Collection<? extends U> clctn) { return this.wrappedSet.addAll(clctn); }
    @Override public boolean addAll(int i, Collection<? extends U> clctn) { throw new UnsupportedOperationException(); }
    @Override public boolean removeAll(Collection<?> clctn) { return this.wrappedSet.removeAll(clctn); }
    @Override public boolean retainAll(Collection<?> clctn) { return this.wrappedSet.retainAll(clctn); }
    @Override public void clear() { this.wrappedSet.clear(); }
    @Override public U get(int i) { throw new UnsupportedOperationException(); }
    @Override public U set(int i, U e) { throw new UnsupportedOperationException(); }
    @Override public void add(int i, U e) { throw new UnsupportedOperationException(); }
    @Override public U remove(int i) { throw new UnsupportedOperationException(); }
    @Override public int indexOf(Object o) { throw new UnsupportedOperationException(); }
    @Override public int lastIndexOf(Object o) { throw new UnsupportedOperationException(); }
    @Override public ListIterator<U> listIterator() { throw new UnsupportedOperationException(); }
    @Override public ListIterator<U> listIterator(int i) { throw new UnsupportedOperationException(); }
    @Override public List<U> subList(int i, int i1) { throw new UnsupportedOperationException(); }
}

...
Set<String> set = getSet(...);
ListViewOfSet<String> listOfNames = new ListViewOfSet<>(set);
...

นี่เป็นคำตอบเดียวที่แก้ปัญหาที่ระบุไว้ในคำถาม!
Lii

คุณสามารถใช้สิ่งนี้ได้อย่างง่ายดายโดยขยาย AbstractList
Lii

1

ฉันพบว่ามันใช้งานได้ดีและมีประโยชน์ในการสร้างรายการจากชุด

ArrayList < String > L1 = new ArrayList < String > ();
L1.addAll(ActualMap.keySet());
for (String x: L1) {
    System.out.println(x.toString());
}

-15
Map<String, List> mainMap = new HashMap<String, List>();

for(int i=0; i<something.size(); i++){
  Set set = getSet(...); //return different result each time
  mainMap.put(differentKeyName, new ArrayList(set));
}

11
คุณไม่ได้หลีกเลี่ยงการสร้างรายการ รหัสนี้คล้ายกับตัวอย่างในคำถามของคุณเล็กน้อย
เทย์เลอร์

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