628

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?

4
  • 1
    I know a way to convert set to list as in Q. I want to avoind creating new list each time in loop. Commented Jan 17, 2012 at 9:46
  • 4
    Why can't you just add the set to mainList? Why do you need to convert the Set into a List? Commented Jan 17, 2012 at 9:46
  • 1
    Is it your intention to create a List<List<?>> Commented Jan 17, 2012 at 9:48
  • 6
    You can't. Your question embodies a contradiction in terms. Commented Jul 21, 2016 at 3:34

15 Answers 15

925

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.

Sign up to request clarification or add additional context in comments.

7 Comments

sorry it was mainMap not list. see question
@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.
@imrantariq: and you want a different list for each key I assume?
@imrantariq: What you are requesting is impossible. read my edit for more details.
It will return NullPointerException in case set is null.
|
511

Use constructor to convert it:

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

5 Comments

He specifically said he wants to avoid this.
@mook Irrelevant, as his requirement is not implementable.
@EJP then his answer needs to say that, instead of simply stating something the OP didn't ask for without any explanation.
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.
This doesn't actually work on Android. Any reason why?
95

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.

8 Comments

If you are using guava, this is handy
Although you are not directly calling the constructor, this method still calls the ArrayList constructor.
If I do not declare a List, how can I use the List created?
Any guess as to why this made this method? It doesn't seem any better than new ArrayList<>([your_set]).
@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.
|
59

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

Note: This way of creating using streams has a memory penalty. If need is to avoid creating new memory, then avoid this solution

5 Comments

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.
exactly! @rrhrg which is better for performance if we use set.parallelStream()?
Collectors.toList() creates a new list in memory
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.
And lets not even talk about the performance penalty for turning to streams in the first place, for such a simple thing.
46

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

1 Comment

This is also what IntelliJ IDEA suggests instead of the streams api.
24

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.

7 Comments

Unfortunately List.copyOf still allocates a new zone of memory to store items.
@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?
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 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.
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".
|
8

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

3 Comments

This is actually the only answer that actually solves the stated problem in the question!
You can implement this pretty easily by extending AbstractList
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.
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); } 

2 Comments

Good answer because it doesn't require new memory allocation, as the OP requested.
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.
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()]))); } 

1 Comment

Corrected size because new Object[0] will hold only one element but new Object[set.size()] will hold all values
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)); 

Comments

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

Comments

2

Recently I found this:

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

3 Comments

Can you expand or elaborate more on this?
Collections.list() creates a new ArrayList: public static <T> ArrayList<T> list(Enumeration<T> e) { ArrayList<T> l = new ArrayList<>(); while (e.hasMoreElements()) l.add(e.nextElement()); return l; }
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.
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()); } 

Comments

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

Comments

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

2 Comments

You haven't avoided creating the list. This code is trivially similar to the sample in your question.
But it doesn't answer your quezon, not that there is an answer.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.