Перетворити "Установити в список" без створення нового списку


503

Я використовую цей код для перетворення 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
Чи маєте Ви намір створити Список <Список <? >>
Hiery Nomus

5
Ви не можете. Ваше запитання втілює протиріччя в термінах.
Маркіз Лорн

Відповіді:


802

Можна скористатися методом List.addAll () . Він приймає колекцію як аргумент, а ваш набір - колекцію.

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

EDIT: як відповідь на редагування питання.
Неважко помітити, що якщо ви хочете мати значення a Mapз Lists як значення, для того щоб мати k різні значення, вам потрібно створити k різних списків.
Таким чином: Ви взагалі не можете уникнути створення цих списків, списки доведеться створити.

Можлива обхід:
Оголосіть свій Mapяк Map<String,Set>або Map<String,Collection>замість цього та просто вставте свій набір.


1
вибачте, що це mainMap не в списку. див. питання
Мухаммед Імран Тарік

@imrantariq: чи differentKeyNameзмінюється кожна ітерація? Ви дійсно хочете something.size()різних можливих значень у ваших картах? Неважко помітити, що карта зі kсписками як значеннями повинна створювати принаймні kсписки.
Аміт

@imrantariq: а ви хочете, щоб я вважав, що для кожного ключа інший список?
Аміт

@imrantariq: Те, що ви просите, неможливо. читати мою редакцію для отримання більш детальної інформації.
Аміт

Він поверне NullPointerException у випадку, якщо набір нульовий.
w35l3y

411

Використовуйте конструктор для його перетворення:

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

21
Він спеціально сказав, що хоче цього уникнути.
mapeters

3
@mook Irrelevant, оскільки його вимога не виконується.
Маркіз Лорн

16
@EJP, тоді його відповідь повинна сказати, що замість того, щоб просто заявити те, що ОП не вимагало без будь-яких пояснень.
mapeters

він уникає цього, що конструктор використовує System.arrayCopy, який робить дрібні копії, тобто він копіює лише посилання об'єктів у масив, який використовується для створення списку. Якщо порівнювати обидві колекції, ви побачите, що вони містять посилання на одні і ті ж об’єкти.
Губатрон

Насправді це не працює на Android. Будь-яка причина чому?
kbluue

84

Також із бібліотеки Guava Collect ви можете використовувати newArrayList(Collection):

Lists.newArrayList([your_set])

Це було б дуже схоже на попередню відповідь від amit , за винятком того, що вам не потрібно декларувати (або призначати) будь-який listоб'єкт.


1
Якщо ви використовуєте гуаву, це зручно
vsingh

6
Хоча ви безпосередньо не викликаєте конструктор, цей метод все ще викликає ArrayListконструктор.
glen3b

Якщо я не оголошую Список, як я можу використовувати Створений список?
Корай Тугай

@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

точно! @rrhrg, що краще для продуктивності, якщо ми використовуємо set.parallelStream ()?
gaurav

31

найпростіше рішення

Я хотів дуже швидким способом перетворити свій набір у List і повернути його, так що в одному рядку я це зробив

 return new ArrayList<Long>(mySetVariable);

1
Це також пропонує IntelliJ IDEA замість потоків api.
Бен

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

Виправлений розмір, оскільки новий Object [0] буде містити лише один елемент, але новий Object [set.size ()] буде містити всі значення
rajadilipkolli

5

Я б робив:

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

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

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
Чи можете ви про це розширити чи детальніше?
Вандал

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; }
Артем Луканін

2

Заради повноти ...

Скажіть , що ви дійсно хочете , щоб обробити Mapзначення , як Listз, але ви хочете , щоб уникнути копіювання Setв Listкожен момент часу.

Наприклад, можливо, ви викликаєте одну функцію бібліотеки, яка створює Set, але ви передаєте свій Map<String, List<String>>результат (погано розроблена, але не з ваших рук) функція бібліотеки, яка займає лише те Map<String, List<String>>, хоча якимось чином ви знаєте, що операції виконуються з Lists однаково застосовні для будь-якого Collection(і, отже, будь-якого Set). І чомусь вам потрібно уникати швидкості / накладних витрат на копіювання кожного набору в список.

У цьому випадку супер ніші, залежно від поведінки (можливо непізнаваної), яку потребує функція бібліотеки у ваших Listсистемах, ви зможете створити List представлення для кожного набору. Зауважте, що це по суті небезпечно (оскільки вимоги до функцій бібліотеки від кожного Listможуть, мабуть, змінитися, не знаючи), тому слід віддавати перевагу іншому рішенню. Але ось як би ви це зробили.

Ви б створили клас, який реалізує Listінтерфейс, приймає Setконструктор і призначає поле "Встановити", а потім використовує цей внутрішній Setдля реалізації ListAPI (наскільки це можливо та бажано).

Зауважте, що деяка поведінка списку, яку ви просто не зможете наслідувати, не зберігаючи елементи як а 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);
...

Це насправді єдина відповідь, яка фактично вирішує заявлену проблему у питанні!
Лій

Ви можете реалізувати це досить легко, продовживши AbstractList
Лія

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
Але це не відповідає вашій quezon, не те, що є відповідь.
Маркіз Лорн
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.