【问题标题】: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>
  • 你不能。你的问题体现了一个矛盾。

标签: java performance list set


【解决方案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

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

【讨论】:

  • 不幸的是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));
          }
          

          【讨论】:

          • 你没有避免创建列表。此代码与您问题中的示例非常相似。
          • 但它没有回答你的问题,并不是说有答案。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-01-29
          • 1970-01-01
          • 2022-11-12
          • 2013-12-16
          • 2017-02-06
          • 2019-05-07
          • 1970-01-01
          相关资源
          最近更新 更多