用 Java 定义固定大小的列表

有没有可能定义一个固定大小为100的列表?如果没有,为什么这在 Java 中不可用?

181509 次浏览

如果我没记错的话,这个应该可以:

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

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

可以。您可以将一个 java 数组传递给 AsList (Object [])

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

不能向 fixedSizeList 插入新的 String (它已经有100个元素)。您只能像这样设置它的值:

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

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

如果需要一些灵活性,可以创建一个监视列表大小的类。

这里有一个简单的例子,您需要覆盖所有改变列表状态的方法。

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

Java 列表是对象的集合... 列表的元素。列表的大小是该列表中的元素数。如果希望固定大小,则意味着不能添加或删除元素,因为添加或删除元素将违反“固定大小”约束。

实现“固定大小”列表的最简单方法(如果这真的是您想要的!)是将元素放入数组中,然后 Arrays.asList(array)创建列表包装器。包装器允许您执行诸如 getset之类的操作,但是 addremove操作将抛出异常。

如果您想为现有列表创建一个固定大小的包装器,那么可以使用 Apache commons FixedSizeList类。但是请注意,这个包装器不能阻止其他内容改变原始列表的大小,如果发生这种情况,那么被包装的列表可能会反映这些改变。


另一方面,如果您真的想要一个对其大小有固定限制(或限制)的列表类型,那么您需要创建自己的 List 类来实现这一点。例如,您可以创建一个包装器类,它在各种 add/addAllremove/removeAll/retainAll操作中实现相关的检查。(在迭代器 remove方法中,如果它们受支持的话。)

那么,为什么 Java集合框架不实施这些措施呢:

  1. 需要它的用例很少。
  2. 在需要这样做的用例中,对于操作试图突破限制时要做什么有不同的要求; 例如,抛出异常、忽略操作、丢弃一些其他元素以腾出空间。
  3. 带有限制的列表实现可能会给辅助方法带来问题,例如 Collections.sort

对于固定大小的 List,一个典型的替代方案是 Java 数组。默认情况下,列表在 Java 中允许增长/缩小。但是,这并不意味着您不能拥有固定大小的 List。您需要做一些工作并创建一个自定义实现。

您可以使用 clear、 add 和 delete 方法的自定义实现来扩展 数组列表

例如:。

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

FixedSizeList

是的,

Apache Commons Apache Commons 库提供的 FixedSizeList类不支持 addremoveclear方法(但是 set 方法是允许的,因为它不修改 List的大小)。Eclipse 集合中的 FixedSizeList也是如此。如果尝试调用其中一个方法,则列表的大小保持不变。

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

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

如果希望获得指定列表的不可修改视图,可以使用 unmodifiableList对内部列表的只读访问

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

如果你想使用 数组列表或者 LinkedList,答案似乎是否定的。尽管 Java 中有一些类可以设置固定的大小,比如 优先队列、 ArrayList 和 LinkedList 就不行,因为这两个类没有指定容量的构造函数。

如果您想坚持使用 ArrayList/LinkedList,一个简单的解决方案是每次手动检查大小。

public void fixedAdd(List<Integer> list, int val, int size) {
list.add(val);
if(list.size() > size) list.remove(0);
}

在这种情况下,LinkedList 比 ArrayList 更好。假设有许多值要添加,但是列表大小很小,那么将有许多删除操作。原因是从数组列表中删除的成本是 O (N) ,但对于 LinkedList 只有 O (1)。

JDK 的公共 java.util.List子类没有提供一个固定大小的特性,因此没有成为 List规范的一部分。
您只能在处理非常特定需求的 Queue子类(例如 ArrayBlockingQueue,一个由数组支持的有界阻塞队列)中找到它。

在 Java 中,使用 List类型,您可以根据以下两种情况实现它:

1)固定列表大小始终是实际大小和最大大小。

听起来像是数组定义。因此,返回由指定数组支持的固定大小列表的 Arrays.asList()就是您要寻找的。和数组一样,你既不能增加也不能减小它的大小,只能改变它的内容。因此不支持添加和删除操作。

例如:

Foo[] foosInput= ...;
List<Foo> foos = Arrays.asList(foosInput);
foos.add(new Foo()); // throws an Exception
foos.remove(new Foo()); // throws an Exception

它还可以将集合作为输入,但首先我们将其转换为一个数组:

Collection<Foo> foosInput= ...;
List<Foo> foos = Arrays.asList(foosInput.toArray(Foo[]::new)); // Java 11 way
// Or
List<Foo> foos = Arrays.asList(foosInput.stream().toArray(Foo[]::new)); // Java 8 way

2)列表内容一经创建就不为人所知。所以你的意思是固定大小列表它的最大大小。

您可以使用继承(extends ArrayList) ,但是您应该优先考虑组合,因为它不允许您的类与此实现的实现细节耦合,并且还提供了关于修饰/组合实现的灵活性。

有了 番石榴转发类,你可以做到:

import com.google.common.collect.ForwardingList;


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


private final List<T> delegate;
private final int maxSize;


public FixedSizeList(List<T> delegate, int maxSize) {
this.delegate = delegate;
this.maxSize = maxSize;
}


@Override protected List<T> delegate() {
return delegate;
}


@Override public boolean add(T element) {
assertMaxSizeNotReached(1);
return super.add(element);
}


@Override public void add(int index, T element) {
assertMaxSizeNotReached(1);
super.add(index, element);
}


@Override public boolean addAll(Collection<? extends T> collection) {
assertMaxSizeNotReached(collection.size());
return super.addAll(collection);
}


@Override public boolean addAll(int index, Collection<? extends T> elements) {
assertMaxSizeNotReached(elements.size());
return super.addAll(index, elements);
}


private void assertMaxSizeNotReached(int size) {
if (delegate.size() + size >= maxSize) {
throw new RuntimeException("size max reached");
}
}


}

使用它:

List<String> fixedSizeList = new FixedSizeList<>(new ArrayList<>(), 3);
fixedSizeList.addAll(Arrays.asList("1", "2", "3"));
fixedSizeList.add("4");  // throws an Exception

注意,通过组合,您可以在任何 List实现中使用它:

List<String> fixedSizeList = new FixedSizeList<>(new LinkedList<>(), 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

这个应该很有用。它永远不会超过最初的大小。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

是的是可能的:

List<Integer> myArrayList = new ArrayList<>(100);

现在 myArrayList 的初始容量是100

根据传递给构建器(Collection<T>T[])的 T元素的容器类型,您需要以下任何一种类型:

  • 如果是现有的 Collection<T> YOUR_COLLECTION:
Collections.unmodifiableList(new ArrayList<>(YOUR_COLLECTION));
  • 如果是现有的 T[] YOUR_ARRAY:
Arrays.asList(YOUR_ARRAY);

就这么简单

要获得一个固定大小的列表,你可以简单地使用 Stream API。这将导致一个固定大小的列表:

    List<Integer> list = Arrays.stream(new int[100])
.boxed()
.collect(Collectors.toList());

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

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