如何将一个集合转换为列表?

348
我正在使用Apache Collections库中的TreeBidiMap。我想按值排序,这些值是doubles
我的方法是使用以下代码检索值的Collection
Collection coll = themap.values();

自然地运行良好。

主要问题:我现在想知道如何将coll转换/强制转换(不确定哪个是正确的)为List,以便可以对其进行排序?

然后,我打算迭代已排序的List对象,它应该是有序的,并使用themap.getKey(iterator.next())TreeBidiMap (themap)获取适当的键,其中迭代器将遍历doubles列表。


5
你可以通过直接使用某种SortedMap来避免这一步骤,使得条目按照使用的键的自然顺序排序。Java自带的TreeMap实现了SortedMap接口。 - Axel Knauf
TreeBidiMap是一个OrderedMap,顺序应该没问题。问题所需的排序是基于值而不是键。 - Vlasec
11个回答

523
List list = new ArrayList(coll);
Collections.sort(list);
如Erel Segal Halevi在下面所说,如果coll已经是一个列表,你可以跳过第一步。但这取决于TreeBidiMap的内部情况。
List list;
if (coll instanceof List)
  list = (List)coll;
else
  list = new ArrayList(coll);

4
请注意,这两种方法会产生不同的副作用:将集合转换为列表并进行排序也会对原始集合进行排序,而创建副本则不会。 - Barney
如果反复使用此方法,性能会大大降低。请参阅我的答案,其中提供了一种即时解决方案,它涉及自定义集合。 - Vlasec
这并没有解决当map.values()返回一个“内部类”集合的情况。编译器报告Collections.sort(List <T>)不接受Collections.sort(List<InnerClass>)。解决方案甚至是使用: List<InnerClass> list = map.values().stream().collect(Collectors.toList()) - Pereira

114

这样做应该可以,调用接受 Collection 的 ArrayList 构造函数

List theList = new ArrayList(coll);

34

我相信你可以这样写:

coll.stream().collect(Collectors.toList())

更好的避免类型转换的方法 - Stackee007
太好了!这解决了我的问题。我的map.values()返回一个“内部类”集合。编译器报告Collections.sort(List<T>)不接受Collections.sort(List<InnerClass>)。 - Pereira
在我的 Android 应用中没有起作用。需要最低 API 24。 - ansh sachdeva

34

我认为Paul Tomblin的答案可能会浪费资源,如果coll已经是一个列表,那么它将创建一个新的列表并复制所有元素。如果coll包含许多元素,这可能需要很长时间。

我的建议是:

List list;
if (coll instanceof List)
  list = (List)coll;
else
  list = new ArrayList(coll);
Collections.sort(list);

14

Java 10引入了List#copyOf,它返回不可修改的List,同时保留顺序:

List<Integer> list = List.copyOf(coll);

10
Collections.sort( new ArrayList( coll ) );

缺少访问ArrayList的引用吗? - Zach Scrivena
@Zach:嗯,说得好。我知道我将其标记为CW的原因。顺便说一句,Paul的答案是正确的。我不知道为什么他只有我的uv。 - OscarRyz

5

从Java 8开始...

你可以使用StreamsCollectors.toCollection()将Collection转换为任何集合(即List,Set和Queue)。

考虑以下示例映射

Map<Integer, Double> map = Map.of(
    1, 1015.45,
    2, 8956.31,
    3, 1234.86,
    4, 2348.26,
    5, 7351.03
);

转换为 ArrayList

List<Double> arrayList = map.values()
                            .stream()
                            .collect(
                                Collectors.toCollection(ArrayList::new)
                            );

输出结果:[7351.03, 2348.26, 1234.86, 8956.31, 1015.45]

按升序排列的排序数组列表

List<Double> arrayListSortedAsc = map.values()
                                        .stream()
                                        .sorted()
                                        .collect(
                                            Collectors.toCollection(ArrayList::new)
                                        );

输出: [1015.45, 1234.86, 2348.26, 7351.03, 8956.31]

转换为降序排列的 ArrayList

List<Double> arrayListSortedDesc = map.values()
                                        .stream()
                                        .sorted(
                                            (a, b) -> b.compareTo(a)
                                        )
                                        .collect(
                                            Collectors.toCollection(ArrayList::new)
                                        );

输出: [8956.31, 7351.03, 2348.26, 1234.86, 1015.45]

转化为链表

List<Double> linkedList = map.values()
                                .stream()
                                .collect(
                                    Collectors.toCollection(LinkedList::new)
                                );

输出:[7351.03,2348.26,1234.86,8956.31,1015.45]

转换为 HashSet

Set<Double> hashSet = map.values()
                            .stream()
                            .collect(
                                Collectors.toCollection(HashSet::new)
                            );

输出:[2348.26,8956.31,1015.45,1234.86,7351.03]

转换为优先队列

PriorityQueue<Double> priorityQueue = map.values()
                                            .stream()
                                            .collect(
                                                Collectors.toCollection(PriorityQueue::new)
                                            );

输出: [1015.45, 1234.86, 2348.26, 8956.31, 7351.03]

参考资料

Java - 包 java.util.stream

Java - 包 java.util


4

@Kunigami: 我认为你可能对Guava的newArrayList方法有所误解。它不会检查Iterable是否是List类型,而是直接返回给定的List。它总是创建一个新的列表:

@GwtCompatible(serializable = true)
public static <E> ArrayList<E> newArrayList(Iterable<? extends E> elements) {
  checkNotNull(elements); // for GWT
  // Let ArrayList's sizing logic work, if possible
  return (elements instanceof Collection)
      ? new ArrayList<E>(Collections2.cast(elements))
      : newArrayList(elements.iterator());
}

这个问题为什么没有更多的赞?Kunigami的答案是不正确的(因为它对底层实现做了一些假设)。 - GreenieMeanie

1
你所请求的操作代价相当高,确保你不需要经常执行它(例如在一个周期内)。
如果你需要它保持排序并经常更新,可以创建一个自定义集合。例如,我想出了一个具有你的 TreeBidiMapTreeMultiset 的集合。只实现你需要的内容并关心数据完整性。
class MyCustomCollection implements Map<K, V> {
    TreeBidiMap<K, V> map;
    TreeMultiset<V> multiset;
    public V put(K key, V value) {
        removeValue(map.put(key, value));
        multiset.add(value);
    }
    public boolean remove(K key) {
        removeValue(map.remove(key));
    }
    /** removes value that was removed/replaced in map */
    private removeValue(V value) {
        if (value != null) {
            multiset.remove(value);
        }
    }
    public Set<K> keySet() {
        return Collections.unmodifiableSet(map.keySet());
    }
    public Collection<V> values() {
        return Collections.unmodifiableCollection(multiset);
    }
    // many more methods to be implemented, e.g. count, isEmpty etc.
    // but these are fairly simple
}

这样,您将获得一个经过排序的Multiset,从values()返回。但是,如果您需要它是一个列表(例如,您需要类似数组的get(index)方法),则需要更复杂的东西。
为简洁起见,我只返回不可修改的集合。@Lino提到的是正确的,如果直接修改keySet或values集合会使其不一致。我不知道任何一致的方法来使值可变,但如果使用上面的MyCustomCollection类中的remove方法,则keySet可以支持remove。

keySet()values()是对原始Map的视图,因此当它们被修改时,支持它们的Map也需要被修改。您的解决方案不支持这一点。 - Lino
1
是的,你说得对。要做到正确,我需要创建至少两个更多的集合类来确保keySet和values都由这两个集合支持。我会更新我的答案。 - Vlasec

1
使用流:
someCollection.stream().collect(Collectors.toList())

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接