在Java中定义一个固定大小的列表

73

在Java中是否可以定义一个固定大小为100的列表?如果不能,为什么Java不支持这个功能?


2
你可以设置 ArrayList 的初始大小,但如果你添加更多的元素,它会自动扩展。 - Joe Phillips
8
固定还是最大值?您希望列表不能超过100个项目吗?如果尝试使用add(),应该发生什么?抛出异常、无操作还是舍弃现有元素以保留新元素。 - Bozho
10
这些动物不是被称为“数组”吗? - Hovercraft Full Of Eels
3
数组不符合List接口的规范。 ;) - Bozho
2
@Bozho:不,它们不需要,但考虑到他的问题中提供的有限信息,谁知道他是否需要这样做,并且它们非常强大。如果他只需要一些集合来保存100个项目,不多也不少,那么他可以使用数组。如果不是这样,他需要非常澄清他的问题。 - Hovercraft Full Of Eels
显示剩余3条评论
15个回答

64

如果我的记忆没错,这应该就可以了:

List<MyType> fixed = Arrays.asList(new MyType[100]);

1
如果你不断添加内容,它会保持在100的大小吗? - fastcodejava
16
@fastcodejava - 您无法在固定长度的列表中使用add方法,因为它已经有了100个空条目。请使用set方法来设置值。请注意保持原意,使翻译更加通俗易懂,不提供额外的解释或信息。 - McDowell
4
如果MyType是参数化类型,则此方法无法起作用。例如,List<List<String>> fixed = Arrays.asList(new List<String>[100]);会生成编译错误(在早期的Java版本中,会生成警告)。请参阅Java教程中的泛型限制 - Ted Hopp
Arrays.asList() 总是创建一个不可变的列表。 - DaithiG
1
@Daithi - 不正确,文档说明“固定大小列表”,其元素可以更改,只是大小不变。 - user15358848
显示剩余2条评论

35
Java列表是一组对象... 列表的元素。列表的大小是该列表中的元素数量。如果您希望该大小固定,那么这意味着您无法添加或删除元素,因为添加或删除元素将违反您的“固定大小”约束。
实现“固定大小”列表的最简单方法(如果确实需要!)是将元素放入数组中,然后使用Arrays.asList(array)创建列表包装器。包装器将允许您执行getset等操作,但addremove操作将引发异常。
如果您想为现有列表创建固定大小的包装器,则可以使用Apache commons FixedSizeList 类。但请注意,此包装器无法阻止其他内容更改原始列表的大小,如果发生这种情况,则封装的列表将反映这些更改。
另一方面,如果您确实需要一个具有固定大小限制(或限制)的列表类型,则需要创建自己的List类来实现此功能。例如,您可以创建一个包装器类,在各种add / addAll和remove / removeAll / retainAll操作中实现相关检查。(如果支持,则还需在迭代器remove方法中执行。)
那么为什么Java集合框架不实现这些呢?我认为原因如下:
1.需要这种用例的情况很少。
2.需要这种用例的情况下,对于尝试打破限制的操作,有不同的要求;例如,抛出异常、忽略操作、丢弃其他元素以腾出空间等。
3.带有限制的列表实现可能对辅助方法造成问题;例如Collections.sort。

23

FixedSizeList

是的,

Apache Commons库提供了FixedSizeList类,该类不支持addremoveclear方法(但允许使用set方法,因为它不会修改List的大小)。Eclipse Collections中的FixedSizeList也是如此。如果您尝试调用其中一个方法,则列表的大小仍然相同。

要创建固定大小的列表,只需调用:

List<YourType> fixed = FixedSizeList.decorate(Arrays.asList(new YourType[100]));

如果您想要查看指定列表的不可修改视图,或只读访问内部列表,可以使用 unmodifiableList

List<YourType> unmodifiable = java.util.Collections.unmodifiableList(internalList);

18
如果Arrays.asList的结果已经是一个固定大小的列表,为什么您要对其进行装饰呢? - PhoneixS
5
是的,FixedSizeList.decorate(...) 是用于包装那些尚未固定大小的列表的。在这里使用它是多余的。 - Stephen C

18

可以。您可以将Java数组传递给 Arrays.asList(Object[])

List<String> fixedSizeList = Arrays.asList(new String[100]);

您无法向FixedSizeList中插入新的字符串(它已经有100个元素)。您只能像这样设置其值:

fixedSizeList.set(7, "new value");

这样你就有了一个固定大小的列表。这个东西就像一个数组一样运行,我想不出使用它的好理由。我很想知道为什么你希望你的固定大小的集合是一个列表而不是只使用一个数组。


我的理由和杰弗里的一样。我很震惊地发现你不能有这样的代码行: T[] myGenericArray = new T[5]; - Swiftslide
1
太好了。根据需求,我们需要将"String"替换为Object类。谢谢。 - Debarati

11
通常,固定大小的列表的替代品是Java数组。在Java中,列表默认允许增长/缩小。但这并不意味着您不能拥有固定大小的列表。您需要做一些工作并创建自定义实现。
您可以通过自定义clear、add和remove方法来扩展ArrayList
例如:
import java.util.ArrayList;

public class FixedSizeList<T> extends ArrayList<T> {

    public FixedSizeList(int capacity) {
        super(capacity);
        for (int i = 0; i < capacity; i++) {
            super.add(null);
        }
    }

    public FixedSizeList(T[] initialElements) {
        super(initialElements.length);
        for (T loopElement : initialElements) {
            super.add(loopElement);
        }
    }

    @Override
    public void clear() {
        throw new UnsupportedOperationException("Elements may not be cleared from a fixed size List.");
    }

    @Override
    public boolean add(T o) {
        throw new UnsupportedOperationException("Elements may not be added to a fixed size List, use set() instead.");
    }

    @Override
    public void add(int index, T element) {
        throw new UnsupportedOperationException("Elements may not be added to a fixed size List, use set() instead.");
    }

    @Override
    public T remove(int index) {
        throw new UnsupportedOperationException("Elements may not be removed from a fixed size List.");
    }

    @Override
    public boolean remove(Object o) {
        throw new UnsupportedOperationException("Elements may not be removed from a fixed size List.");
    }

    @Override
    protected void removeRange(int fromIndex, int toIndex) {
        throw new UnsupportedOperationException("Elements may not be removed from a fixed size List.");
    }
}

5
创建一个大小为100的数组。如果需要使用List接口,则调用Arrays.asList。它将返回由数组支持的固定大小列表。

3
如果您需要一些灵活性,可以创建一个监视列表大小的类。
这是一个简单的例子。您需要重写所有更改列表状态的方法。
public class LimitedArrayList<T> extends ArrayList<T>{
    private int limit;

    public LimitedArrayList(int limit){
        this.limit = limit;
    }

    @Override
    public void add(T item){
        if (this.size() > limit)
            throw new ListTooLargeException();
        super.add(item);
    }

    // ... similarly for other methods that may add new elements ...

1
add(int index, T element) 方法也需要被重写。 - shams
1
@shams:这就是为什么我说“你需要覆盖所有改变列表状态的方法”... - Jeremy
是的,我认为处理这两个足矣。 add是标准接口中唯一可以增加List大小的方法。我想有四个但另外两个(addAll)也会调用add*方法。 - shams
1
@shams:说得好,但我不确定我会依赖于那个,因为没有合同规定其他人必须依赖于简单的add()。为了安全起见,我会重写所有四个add方法。 - Jeremy
检查应该是:this.size() == limit - grasshopper
显示剩余3条评论

3
你可以像这样定义一个通用函数:
@SuppressWarnings("unchecked")
public static <T> List<T> newFixedSizeList(int size) {
    return (List<T>)Arrays.asList(new Object[size]);
}

并且
List<String> s = newFixedSizeList(3);  // All elements are initialized to null
s.set(0, "zero");
s.add("three");  // throws java.lang.UnsupportedOperationException

1
这应该能很好地工作。它永远不会超出初始大小。toList方法将按正确的时间顺序提供条目。这是在groovy中完成的 - 但将其转换为适当的java应该很容易。
static class FixedSizeCircularReference<T> {
    T[] entries
    FixedSizeCircularReference(int size) {
        this.entries = new Object[size] as T[]
        this.size = size
    }
    int cur = 0
    int size
    void add(T entry) {
        entries[cur++] = entry
        if (cur >= size) {
            cur = 0
        }
    }
    List<T> asList() {
        List<T> list = new ArrayList<>()
        int oldest = (cur == size - 1) ? 0 : cur
        for (int i = 0; i < this.entries.length; i++) {
            def e = this.entries[oldest + i < size ? oldest + i : oldest + i - size]
            if (e) list.add(e)
        }
        return list
    }
}

FixedSizeCircularReference<String> latestEntries = new FixedSizeCircularReference(100)
latestEntries.add('message 1') 
// .....
latestEntries.add('message 1000') 
latestEntries.asList() //Returns list of '100' messages

0
要获取固定大小的列表,您可以简单地使用Stream API。这将导致一个固定大小的列表:
    List<Integer> list = Arrays.stream(new int[100])
                            .boxed()
                            .collect(Collectors.toList());

或者老式的方法,这将导致由指定数组支持的固定大小列表:

    List<Integer> list = Arrays.asList(new Integer[100]);

不,Collectors.toList() 的API说明中指出,“返回的List的类型、可变性等没有任何保证”,因此返回的列表可能支持向其中添加更多元素。 - Izruo

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