将集合转换为列表而不创建新列表

Posted

技术标签:

【中文标题】将集合转换为列表而不创建新列表【英文标题】:Convert Set to List without creating new List 【发布时间】:2012-02-12 02:57:24 【问题描述】:

我正在使用此代码将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);

我想避免在循环的每次迭代中创建一个新列表。这可能吗?

【问题讨论】:

我知道一种在 Q 中将集合转换为列表的方法。我想避免每次循环创建新列表。 为什么不能只将集合添加到 mainList?为什么需要将 Set 转换为 List? 您是否打算创建一个 List> 你不能。你的问题体现了一个矛盾。 【参考方案1】:

您可以使用List.addAll() 方法。它接受一个集合作为参数,并且你的集合是一个集合。

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

编辑: 作为对问题编辑的回应。 很容易看出,如果你想有一个MapLists 作为值,为了有k 个不同的值,你需要创建k 个不同的列表。 因此:您根本无法避免创建这些列表,必须创建这些列表。

可能的解决方法: 将您的Map 声明为Map&lt;String,Set&gt;Map&lt;String,Collection&gt;,然后插入您的集合。

【讨论】:

很抱歉,这是 mainMap 未列出。查看问题 @imrantariq:differentKeyName 每次迭代都会改变吗?你真的想要 something.size() 在你的地图中使用不同的可能值吗?不难看出,以k 列表为值的映射至少需要创建k 列表。 @imrantariq:你想我假设的每个键都有一个不同的列表吗? @imrantariq:你要求的是不可能的。阅读我的编辑了解更多详情。 如果set为null,会返回NullPointerException。【参考方案2】:

使用构造函数进行转换:

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

【讨论】:

他明确表示要避免这种情况。 @mook 不相关,因为他的要求无法实现。 他在避免它,该构造函数使用 System.arrayCopy,它会进行浅拷贝,这意味着它只将对象的引用复制到用于创建列表的数组中。如果比较这两个集合,您会发现它们都包含对相同对象的引用。 这实际上不适用于 android。有什么原因吗?【参考方案3】:

同样来自 Guava Collect 库,你可以使用newArrayList(Collection)

Lists.newArrayList([your_set])

这与 amit 的上一个答案非常相似,只是您不需要声明(或实例化)任何 list 对象。

【讨论】:

如果你使用的是番石榴,这很方便 虽然不是直接调用构造函数,但是这个方法还是调用了ArrayList构造函数。 如果不声明List,创建的List如何使用? 您猜猜为什么会采用这种方法?它似乎并不比new ArrayList&lt;&gt;([your_set]) 更好。 @DavidS:工厂(查找概念和优点)样式方法通常更灵活通用,更易于编写和处理。例如。假设您在变量中提供了一个 null 集,并希望在这种情况下获得一个 null 列表。【参考方案4】:

我们可以在 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());

【讨论】:

为了可读性,不推荐。例如,IntelliJ 建议“new ArrayList(set)”并列出 20 多个类似的代码示例,可以用相同的方式替换。 没错! @rrhrg 如果我们使用 set.parallelStream() 哪个对性能更好? Collectors.toList() 在内存中创建一个新列表 直到今天,我不知道这条评论是如何获得如此多的支持的,尤其是在做一件简单的事情时,但正如其他 cmets 所指出的那样,它更复杂。此外,它正在创建一个新列表,这是作者试图避免的。 对于这样一个简单的事情,我们甚至不要谈论首先转向流的性能损失。【参考方案5】:

最简单的解决方案

我想要一种非常快速的方法来将我的集合转换为 List 并返回它,所以我在一行中做到了

 return new ArrayList<Long>(mySetVariable);

【讨论】:

这也是 IntelliJ IDEA 建议的,而不是流 api。【参考方案6】:

由于到目前为止还没有提到,从 Java 10 开始,您可以使用新的 copyOf 工厂方法:

List.copyOf(set);

来自Javadoc:

返回一个unmodifiable List,其中包含给定集合的元素,按其迭代顺序。

请注意,这会在后台创建一个新列表(准确地说是ImmutableCollections$ListN

    在给定的集合上调用Collection#toArray(),然后 将这些对象放入一个新数组中。

【讨论】:

不幸的是List.copyOf 仍然分配一个新的内存区域来存储项目。 @cdalxndr 是的,就像 95% 的其他答案一样,这就是为什么我为了完整起见添加了这个。不过,如果您愿意,我很乐意添加免责声明? 因此,我对 95% 的其他答案投了反对票。 Ofc 这个答案应该包括它不回答 OP 问题要求的信息。 @cdalxndr 根据提供的 sn-p,你无法判断通过getSet(...) 检索到的集合实现类型,如果它实际上是排序的。还不清楚地图创建后会发生什么。标题说转换设置为列表而不创建新列表。 IMO 如果创建一个新列表违反了要求,那么转换为不同的类型也是如此。无论如何,我认为我们可以同意不同意。 请注意,最坏​​的情况是,当有一个大集合要转换为列表时,复制所需的内存复杂度为 O(n)(内存的两倍)。因此,与 noop 多态性或“视图包装器”相反,它不是“可忽略的”。【参考方案7】:

我愿意:

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

【讨论】:

很好的答案,因为它不需要按照 OP 的要求分配新的内存。 这并没有解决 OP 要求的从 SetList 的转换。不幸的是,尚不清楚Collection 是否足够或是否确实需要List 功能。【参考方案8】:

您可以使用这一行更改: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()])));
  

【讨论】:

修正了大小,因为 new Object[0] 将只包含一个元素,但 new Object[set.size()] 将包含所有值【参考方案9】:

Java 8 提供了使用流的选项,您可以从Set&lt;String&gt; 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 不保证。如前所述here:

不保证类型、可变性、可序列化或 返回列表的线程安全;如果对退货有更多的控制权 列表是必需的,使用 toCollection(Supplier)。

如果您想始终确保,您可以请求一个实例,具体如下:

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

【讨论】:

【参考方案10】:

我创建了简单的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;

【讨论】:

【参考方案11】:

最近我发现了这个:

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

【讨论】:

你能详细说明一下吗? Collections.list() 创建一个新的 ArrayList:public static &lt;T&gt; ArrayList&lt;T&gt; list(Enumeration&lt;T&gt; e) ArrayList&lt;T&gt; l = new ArrayList&lt;&gt;(); while (e.hasMoreElements()) l.add(e.nextElement()); return l; Collections.enumeration 是一个视图,不分配新内存,但Collections.list 创建一个新的ArrayList 并将所有项目存储在那里,因此这不是OP想要的。【参考方案12】:

为了完整起见...

假设您真的确实希望将Map 值视为Lists,但您希望避免每次都将Set 复制到List

例如,也许您正在调用一个创建Set 的库函数,但是您将Map&lt;String, List&lt;String&gt;&gt; 结果传递给了一个(设计不佳但无法控制)库函数,该库函数只需要Map&lt;String, List&lt;String&gt;&gt;,即使你知道它对Lists 所做的操作同样适用于任何Collection(因此也适用于任何Set)。而且出于某种原因您需要避免将每个 Set 复制到 List 的速度/内存开销。

在这个超级小众案例中,根据库函数需要从您的Lists 中获得的(可能是不可知的)行为,您可以在每个 Set 上创建一个 Listview .请注意,这本质上是不安全的(因为每个List 的库函数要求可能会在您不知情的情况下发生变化),因此应该首选另一种解决方案。但这就是你的做法。

您将创建一个实现List 接口的类,在构造函数中获取Set 并将该Set 分配给一个字段,然后使用该内部Set 来实现List API(到可能和期望的范围内)。

请注意,如果不将元素存储为 List,您将无法模仿某些 List 行为,而您只能部分模仿某些行为。同样,一般来说,此类不是Lists 的安全替代品。特别是,如果您知道该用例需要与索引相关的操作或 MUTATING 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 来实现这个 过于复杂。除了使用List不完整 实现来防止违反有序元素,最简单的答案是使用Collection 接口,它是ListSet 的基本接口。 【参考方案13】:

我发现这对从集合创建列表非常有用。

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

【讨论】:

【参考方案14】:

您将Set 转换为List 而不添加排序信息(如排序)只是为了将其存储在地图中。

因为Set是无序的且没有添加排序信息,所以不应该使用List,因为它会包含随机排序的数据,并且所有与排序数据相关的方法都是模糊的。

您应该改用Collection 接口,它在地图中同时接受SetList。这样,由于您使用多态性而不是复制数据,因此不需要额外的内存。

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

免责声明:我对 similar 答案的编辑被拒绝,所以我添加了自己的答案和其他信息

【讨论】:

【参考方案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));

【讨论】:

你没有避免创建列表。此代码与您问题中的示例非常相似。 但它没有回答你的问题,并不是说有答案。

以上是关于将集合转换为列表而不创建新列表的主要内容,如果未能解决你的问题,请参考以下文章

插入两个集合而不阻塞

如何将集合转换为列表?

如何将数组列表转换为多维数组

Java将列表转换为集合图[重复]

在这种情况下如何将集合转换为列表

如何将 Cookies 集合转换为通用列表?容易地