598

I am using this code to convert a Set to a 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);
}

I want to avoid creating a new list in each iteration of the loop. Is that possible?

Lii
  • 11,553
  • 8
  • 64
  • 88
Muhammad Imran Tariq
  • 22,654
  • 47
  • 125
  • 190

15 Answers15

901

You can use the List.addAll() method. It accepts a Collection as an argument, and your set is a Collection.

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

EDIT: as respond to the edit of the question.
It is easy to see that if you want to have a Map with Lists as values, in order to have k different values, you need to create k different lists.
Thus: You cannot avoid creating these lists at all, the lists will have to be created.

Possible work around:
Declare your Map as a Map<String,Set> or Map<String,Collection> instead, and just insert your set.

Andrew
  • 5,839
  • 1
  • 51
  • 72
amit
  • 175,853
  • 27
  • 231
  • 333
  • 1
    sorry it was mainMap not list. see question – Muhammad Imran Tariq Jan 17 '12 at 09:51
  • @imrantariq: is `differentKeyName` changing every iteration? Do you actually want `something.size()` different possible values in your maps? It is easy to see that a map with `k` lists as values needs to create at least `k` lists. – amit Jan 17 '12 at 09:55
  • @imrantariq: and you want a different list for each key I assume? – amit Jan 17 '12 at 10:00
  • @imrantariq: What you are requesting is impossible. read my edit for more details. – amit Jan 17 '12 at 10:09
  • It will return NullPointerException in case set is null. – w35l3y Aug 30 '16 at 20:28
  • You could imagine a method that would wrap a set into a list, similar to how you can wrap a list into an immutable list with Collections.unmodifiableList. It would probably not be a good idea to have such an API, since the order may change on inserts, but still – Kire Haglin Oct 19 '16 at 07:40
  • @basheer one-liner solution using the ArrayList constructor is simpler. I don't understand why this answer is so much upvoted – user1075613 Nov 07 '19 at 21:48
483

Use constructor to convert it:

List<?> list = new ArrayList<>(set);
PushpikaWan
  • 2,437
  • 3
  • 14
  • 23
zengsn
  • 5,045
  • 1
  • 14
  • 9
  • 26
    He specifically said he wants to avoid this. – mapeters Jun 28 '16 at 19:47
  • 9
    @mook Irrelevant, as his requirement is not implementable. – user207421 Jul 21 '16 at 03:35
  • 19
    @EJP then his answer needs to say that, instead of simply stating something the OP didn't ask for without any explanation. – mapeters Aug 01 '16 at 21:31
  • 4
    he's avoiding it, that constructor uses System.arrayCopy, which makes shallow copies, meaning, it only copies the references of the objects into the array that's used to create the list. If you compare both collections, you will see that they both contain references to the same objects. – Gubatron Sep 21 '16 at 01:38
  • This doesn't actually work on Android. Any reason why? – kbluue Jun 08 '17 at 02:07
93

Also from Guava Collect library, you can use newArrayList(Collection):

Lists.newArrayList([your_set])

This would be very similar to the previous answer from amit, except that you do not need to declare (or instanciate) any list object.

cнŝdk
  • 31,391
  • 7
  • 56
  • 78
chaiyachaiya
  • 2,617
  • 18
  • 19
  • 2
    If you are using guava, this is handy – vsingh Jul 16 '13 at 18:28
  • 9
    Although you are not directly calling the constructor, this method still calls the `ArrayList` constructor. – glen3b May 17 '14 at 02:42
  • If I do not declare a List, how can I use the List created? – Koray Tugay Jun 30 '14 at 05:52
  • @KorayTugay, well you extract *Lists.newArrayList([your_set])* in a variable (local or global). E.g: List fooList = Lists.newArrayList(setOfFoo) But your question is flawed. If you create a list it is at least implicitly declared (if not explicitly). – chaiyachaiya Jul 01 '14 at 07:33
  • I was confused because in the answer you say: except that you do not need to declare any list object.. – Koray Tugay Jul 01 '14 at 08:10
  • @KorayTugay, true :) . Let's just say "instanciate" or "create" – chaiyachaiya Jul 01 '14 at 09:21
  • 2
    Any guess as to why this made this method? It doesn't seem any better than `new ArrayList<>([your_set])`. – DavidS Feb 20 '15 at 22:17
  • 1
    @DavidS: factory (look up the concept and advantages) style methods are generally more flexible and general, easier to write and handle. E.g. imagine you provide a `null` set in a variable and want to get a `null` list in this case. – Andreas Covidiot Sep 24 '20 at 11:27
56

We can use following one liner in Java 8:

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

Here is one small example:

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());
}
rajadilipkolli
  • 3,475
  • 2
  • 26
  • 49
  • 10
    For readability it's not recommended. For example, IntelliJ suggests "new ArrayList<>(set)" and lists more than 20 similar code samples with can be replaced the same way. – rrhrg Apr 11 '18 at 07:46
  • exactly! @rrhrg which is better for performance if we use set.parallelStream()? – Gaurav May 23 '20 at 00:08
  • `Collectors.toList()` creates a new list in memory – cdalxndr Aug 12 '20 at 15:23
  • 2
    Up to this day, I don't know how this comment got so many upvotes, especially when doing a simple thing, but with more complexity as the other comments pointed out. Also, it's creating a new list, what the author tried to avoid. – Catalin Pirvu Jun 18 '21 at 14:25
  • And lets not even talk about the performance penalty for turning to streams in the first place, for such a simple thing. – GhostCat Oct 14 '21 at 11:44
41

the simplest solution

I wanted a very quick way to convert my set to List and return it, so in one line I did

 return new ArrayList<Long>(mySetVariable);
Basheer AL-MOMANI
  • 14,473
  • 9
  • 96
  • 92
17

Since it hasn't been mentioned so far, as of Java 10 you can use the new copyOf factory method:

List.copyOf(set);

From the Javadoc:

Returns an unmodifiable List containing the elements of the given Collection, in its iteration order.

Note that this creates a new list (ImmutableCollections$ListN to be precise) under the hood by

  1. calling Collection#toArray() on the given set and then
  2. putting these objects into a new array.
beatngu13
  • 7,201
  • 6
  • 37
  • 66
  • 3
    Unfortunately `List.copyOf` still allocates a new zone of memory to store items. – cdalxndr Aug 12 '20 at 15:26
  • @cdalxndr yes, like 95 % of the other answers, which is why I added this for the sake of completeness. However, I‘m happy to add a disclaimer if you want to? – beatngu13 Aug 12 '20 at 16:02
  • I've downvoted 95% of other answers because of this. Ofc this answer should include info that it doesn't answer the requirements of the OP's question. – cdalxndr Aug 12 '20 at 18:07
  • @cdalxndr fair enough, updated the answer accordingly. But when you insist on the OP's requirements, how comes you like/edit [this answer](https://stackoverflow.com/a/8892760/3429133)? This doesn't address the conversion from `Set` to `List`, and it is not clear whether `Collection` is sufficient or if `List` features are indeed required. – beatngu13 Aug 12 '20 at 21:07
  • Set is unordered, so the conversion to List without adding any ordering (like sorting) is still unordered (ordered randomly) so the List class is not required and any of it's features (all List methods are ordered specific) are undefined on randomly ordered List. From the OP's code, no ordering is added, so List should not be used. – cdalxndr Aug 14 '20 at 10:06
  • 1
    @cdalxndr based on the provided snippet, you can't tell what kind of set implementation is retrieved via `getSet(...)`, if it is actually sorted or not. It is also unclear what happens after the map has been created. Title says convert set to list without creating a new list. IMO if creating a new list violates a requirement, so does converting to a different type. Anyway, I think we can agree to disagree. – beatngu13 Aug 14 '20 at 11:32
  • 1
    Note that worst case when there is a single large set to be converted to list, the memory complexity is O(n) (double the memory) required for copying. So it is not "negligible", as opposed to noop polymorphism or a "view wrapper". – cdalxndr Aug 14 '20 at 13:55
5

You could use this one line change: 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()])));
}  
rajadilipkolli
  • 3,475
  • 2
  • 26
  • 49
Saïd
  • 8,780
  • 1
  • 28
  • 28
5

For the sake of completeness...

Say that you really do want to treat the Map values as Lists, but you want to avoid copying the Set into a List each time.

For instance, maybe you are calling one library function that creates a Set, but you are passing your Map<String, List<String>> result to a (poorly-designed but out of your hands) library function that only takes Map<String, List<String>>, even though somehow you know that the operations it does with the Lists are equally applicable to any Collection (and thus any Set). And for some reason you need to avoid the speed/memory overhead of copying each Set to a List.

In this super niche case, depending on the (maybe unknowable) behavior the library function needs out of your Lists, you may be able to create a List view over each Set. Note that this is inherently unsafe (because the library function's requirements from each List could presumably change without you knowing), so another solution should be preferred. But here's how you'd do it.

You'd create a class that implements the List interface, takes a Set in the constructor and assigns that Set to a field, and then uses that internal Set to implement the List API (to the extent possible, and desired).

Note that some List behavior you simply will not be able to imitate without storing the elements as a List, and some behavior you will only partially be able to imitate. Again, this class is not a safe drop-in replacement for Lists in general. In particular, if you know that the use case requires index-related operations or MUTATING the List, this approach would go south very fast.

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);
...
Daniel Avery
  • 159
  • 1
  • 4
  • 1
    This is actually the only answer that actually solves the stated problem in the question! – Lii May 08 '20 at 12:52
  • You can implement this pretty easily by extending AbstractList – Lii May 15 '20 at 06:51
  • 1
    Overcomplicated. Instead of using an **incomplete** implementation of `List` to prevent violation of ordered elements, the simplest answer is to use `Collection` interface that is a base interface for both `List` and `Set`. – cdalxndr Aug 12 '20 at 15:34
5

I would do :

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);
}
Jerome L
  • 607
  • 5
  • 11
  • Good answer because it doesn't require new memory allocation, as the OP requested. – cdalxndr Aug 12 '20 at 15:30
  • 1
    This doesn't address the conversion from `Set` to `List` as requested by the OP. Unfortunately, it is not clear whether `Collection` is sufficient or if `List` features are indeed required. – beatngu13 Aug 12 '20 at 21:09
4

Java 8 provides the option of using streams and you can get a list from Set<String> setString as:

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

Though the internal implementation as of now provides an instance of 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);
    }

but JDK does not guarantee it. As mentioned here:

There are no guarantees on the type, mutability, serializability, or thread-safety of the List returned; if more control over the returned List is required, use toCollection(Supplier).

In case you want to be sure always then you can request for an instance specifically as:

List<String> stringArrayList = setString.stream()
                     .collect(Collectors.toCollection(ArrayList::new));
akhil_mittal
  • 23,309
  • 7
  • 96
  • 95
3

I create simple static method:

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

... or if you want to set type of List you can use:

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

Recently I found this:

ArrayList<T> yourList = Collections.list(Collections.enumeration(yourSet<T>));
Ahmed Ashour
  • 5,179
  • 10
  • 35
  • 56
Stand Yang
  • 47
  • 1
  • 1
    Can you expand or elaborate more on this? – Vandal Dec 27 '16 at 02:18
  • 1
    Collections.list() creates a new ArrayList: `public static ArrayList list(Enumeration e) { ArrayList l = new ArrayList<>(); while (e.hasMoreElements()) l.add(e.nextElement()); return l; }` – Artem Lukanin Jan 31 '18 at 14:29
  • 1
    `Collections.enumeration` is a view and doesn't allocate new memory, but `Collections.list` creates a new `ArrayList` and stores all items there, thus it's not what OP wanted. – cdalxndr Aug 12 '20 at 15:37
0

I found this working fine and useful to create a List from a Set.

ArrayList < String > L1 = new ArrayList < String > ();
L1.addAll(ActualMap.keySet());
for (String x: L1) {
    System.out.println(x.toString());
}
Mohammad Faisal
  • 5,783
  • 15
  • 70
  • 117
Karthic.K
  • 416
  • 5
  • 15
0

You convert Set to List without adding ordering information (like sorting) just to store it in the map.

Because Set is unordered and no ordering information is added, List should not be used, as it will contain randomly ordered data and all it's methods that are related to ordered data will be ambiguous.

You should use Collection interface instead, that accepts both Set and List in the map. This way, no additional memory is required as you use polymorphism instead of copying data.

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

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

Disclaimer: my edit to a similar answer was rejected so I added my own answer with additional information

cdalxndr
  • 1,435
  • 1
  • 15
  • 20
-16
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));
}
Joetjah
  • 6,292
  • 8
  • 55
  • 90
Muhammad Imran Tariq
  • 22,654
  • 47
  • 125
  • 190