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

发布于 2024-10-20 18:16:26 字数 51 浏览 0 评论 0原文

是否可以定义一个固定大小为 100 的列表?如果不是,为什么 Java 中不提供此功能?

Is it possible to define a list with a fixed size that's 100? If not why isn't this available in Java?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

‖放下 2024-10-27 18:16:26

如果没记错的话应该这样做:

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

This should do it if memory serves:

List<MyType> fixed = Arrays.asList(new MyType[100]);
近箐 2024-10-27 18:16:26

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

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

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


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

那么为什么 Java Collections 框架不实现这些呢?这就是我这么认为的原因:

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

A Java list is a collection of objects ... the elements of a list. The size of the list is the number of elements in that list. If you want that size to be fixed, that means that you cannot either add or remove elements, because adding or removing elements would violate your "fixed size" constraint.

The simplest way to implement a "fixed sized" list (if that is really what you want!) is to put the elements into an array and then Arrays.asList(array) to create the list wrapper. The wrapper will allow you to do operations like get and set, but the add and remove operations will throw exceptions.

And if you want to create a fixed-sized wrapper for an existing list, then you could use the Apache commons FixedSizeList class. But note that this wrapper can't stop something else changing the size of the original list, and if that happens the wrapped list will presumably reflect those changes.


On the other hand, if you really want a list type with a fixed limit (or limits) on its size, then you'll need to create your own List class to implement this. For example, you could create a wrapper class that implements the relevant checks in the various add / addAll and remove / removeAll / retainAll operations. (And in the iterator remove methods if they are supported.)

So why doesn't the Java Collections framework implement these? Here's why I think so:

  1. Use-cases that need this are rare.
  2. The use-cases where this is needed, there are different requirements on what to do when an operation tries to break the limits; e.g. throw exception, ignore operation, discard some other element to make space.
  3. A list implementation with limits could be problematic for helper methods; e.g. Collections.sort.
平安喜乐 2024-10-27 18:16:26

FixedSizeList

Apache Commons 库提供 FixedSizeList 类不支持 addremoveclear 方法(但是允许使用 set 方法,因为它不会修改 List 的大小)。同上 FixedSizeListEclipse Collections 中。如果您尝试调用这些方法之一,您的列表将保持相同的大小。

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

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

您可以使用 unmodifyingList 如果您想要指定列表的不可修改视图,或对内部列表的只读访问权限

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

FixedSizeList

Yes,

The Apache Commons library provides the FixedSizeList class which does not support the add, remove and clear methods (but the set method is allowed because it does not modify the List's size). Ditto for FixedSizeList in Eclipse Collections. If you try to call one of these methods, your list remains the same size.

To create your fixed size list, just call

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

You can use unmodifiableList if you want an unmodifiable view of the specified list, or read-only access to internal lists.

List<YourType> unmodifiable = java.util.Collections.unmodifiableList(internalList);
月亮是我掰弯的 2024-10-27 18:16:26

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

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

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

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

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

Yes. You can pass a java array to Arrays.asList(Object[]).

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

You cannot insert new Strings to the fixedSizeList (it already has 100 elements). You can only set its values like this:

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

That way you have a fixed size list. The thing functions like an array and I can't think of a good reason to use it. I'd love to hear why you want your fixed size collection to be a list instead of just using an array.

挽手叙旧 2024-10-27 18:16:26

通常,固定大小列表的替代方案是 Java 数组。 Java 中默认允许列表增长/收缩。但是,这并不意味着您不能拥有固定大小的列表。您需要做一些工作并创建自定义实现。

您可以使用以下自定义实现来扩展 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.");
    }
}

Typically an alternative for fixed size Lists are Java arrays. Lists by default are allowed to grow/shrink in Java. However, that does not mean you cannot have a List of a fixed size. You'll need to do some work and create a custom implementation.

You can extend an ArrayList with custom implementations of the clear, add and remove methods.

e.g.

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.");
    }
}
心欲静而疯不止 2024-10-27 18:16:26

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

Create an array of size 100. If you need the List interface, then call Arrays.asList on it. It'll return a fixed-size list backed by the array.

愚人国度 2024-10-27 18:16:26

如果您想要一些灵活性,请创建一个监视列表大小的类。

这是一个简单的例子。您需要重写所有更改列表状态的方法。

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

If you want some flexibility, create a class that watches the size of the list.

Here's a simple example. You would need to override all the methods that change the state of the list.

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 ...
孤蝉 2024-10-27 18:16:26

您可以像这样定义一个通用函数:

@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

You can define a generic function like this:

@SuppressWarnings("unchecked")
public static <T> List<T> newFixedSizeList(int size) {
    return (List<T>)Arrays.asList(new Object[size]);
}

And

List<String> s = newFixedSizeList(3);  // All elements are initialized to null
s.set(0, "zero");
s.add("three");  // throws java.lang.UnsupportedOperationException
π浅易 2024-10-27 18:16:26

这应该工作得很好。它永远不会增长超过初始大小。 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

This should work pretty nicely. It will never grow beyond the initial size. The toList method will give you the entries in the correct chronological order. This was done in groovy - but converting it to java proper should be pretty easy.

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
橙味迷妹 2024-10-27 18:16:26

您需要以下任一选项,具体取决于传递给构建器的 T 元素容器的类型(CollectionT[]):

  • 如果存在现有Collection; YOUR_COLLECTION
Collections.unmodifyingList(new ArrayList<>(YOUR_COLLECTION));
  • 如果存在现有 T[] YOUR_ARRAY
Arrays.asList(YOUR_ARRAY);

就这么简单

You need either of the following depending on the type of the container of T elements you pass to the builder (Collection<T> or T[]):

  • In case of an existing Collection<T> YOUR_COLLECTION:
Collections.unmodifiableList(new ArrayList<>(YOUR_COLLECTION));
  • In case of an existing T[] YOUR_ARRAY:
Arrays.asList(YOUR_ARRAY);

Simple as that

随波逐流 2024-10-27 18:16:26

如果您想使用 ArrayList 或 < a href="https://docs.oracle.com/javase/8/docs/api/java/util/LinkedList.html" rel="nofollow noreferrer">LinkedList,似乎答案是否定的。虽然java中有一些类可以设置它们固定大小,例如 PriorityQueue,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 更好。假设有很多值要添加,但列表大小很小,就会有很多删除操作。原因是从 ArrayList 中删除的成本是 O(N),但从 LinkedList 中删除的成本仅为 O(1)。

If you want to use ArrayList or LinkedList, it seems that the answer is no. Although there are some classes in java that you can set them fixed size, like PriorityQueue, ArrayList and LinkedList can't, because there is no constructor for these two to specify capacity.

If you want to stick to ArrayList/LinkedList, one easy solution is to check the size manually each time.

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

LinkedList is better than ArrayList in this situation. Suppose there are many values to be added but the list size is quite samll, there will be many remove operations. The reason is that the cost of removing from ArrayList is O(N), but only O(1) for LinkedList.

香橙ぽ 2024-10-27 18:16:26

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),但您应该更喜欢组合,因为它允许您不将您的类与此实现的实现细节耦合,并且还提供了有关装饰/组合的实现的灵活性。

使用 Guava 转发类,你可以这样做:

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

这对于继承是不可能的。

The public java.util.List subclasses of the JDK don't provide a fixed size feature that doesn't make part of the List specification.
You could find it only in Queue subclasses (for example ArrayBlockingQueue, a bounded blocking queue backed by an array for example) that handle very specific requirements.

In Java, with a List type, you could implement it according to two scenarios :

1) The fixed list size is always both the actual and the maximum size.

It sounds as an array definition. So Arrays.asList() that returns a fixed-size list backed by the specified array is what you are looking for. And as with an array you can neither increase nor decrease its size but only changing its content. So adding and removing operation are not supported.

For example :

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

It works also with a collection as input while first we convert it into an array :

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) The list content is not known as soon as its creation. So you mean by fixed size list its maximum size.

You could use inheritance (extends ArrayList) but you should favor composition over that since it allows you to not couple your class with the implementation details of this implementation and provides also flexibility about the implementation of the decorated/composed.

With Guava Forwarding classes you could do :

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

}

And use it :

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

Note that with composition, you could use it with any List implementation :

List<String> fixedSizeList = new FixedSizeList<>(new LinkedList<>(), 3); 
//...

Which is not possible with inheritance.

‖放下 2024-10-27 18:16:26

要获得固定大小的列表,您只需使用 Stream API 即可。这将产生一个固定大小的列表:

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

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

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

To get a fixed-size list, you can simply use the Stream API. This will result in a fixed-size list :

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

Or the old-fashioned way, This will result in a fixed-size list that is backed by the specified array:

    List<Integer> list = Arrays.asList(new Integer[100]);
笑红尘 2024-10-27 18:16:26

例如在包装类中),是使用初始化为 nullArrayList

List<MyGenericType<E>> list = new ArrayList<>(Collections.nCopies(size, null));

另一种变体,特别是如果您只想要通用的固定大小的内部数据源( 设置或不设置值包括检查 null:

if (list.get(index) == null)
    // Not set

设置值必须使用 set(index, ...) 完成。删除意味着使用 set(index, null) 设置回 null。对于流处理,您可能需要添加额外的 filter(Objects::nonNull) 以仅获取“设置”值。

您可以添加、删除和打破固定大小,因此该列表不能暴露给包装类之外的世界,除非它不可修改。

这是创建内部“数组”的一种更简单的方法,可以对值进行一些很好的处理,并且没有 Integer 键控 Map 的额外内存负担。不过,Arrays.asList(...) 可能仍然适合您的目的。

Another variant, especially if you only want a fixed size internal data source (e.g. in a wrapper class) that is generic, is to use an ArrayList initialized to null:

List<MyGenericType<E>> list = new ArrayList<>(Collections.nCopies(size, null));

Checking if a value is set or not consists of checking for null:

if (list.get(index) == null)
    // Not set

Setting a value must be done using set(index, ...). Removing would mean setting back to null with set(index, null). For stream handling, you might want to add an extra filter(Objects::nonNull) to only get "set" values.

You can do add and remove and break the fixed size, so this list cannot be exposed to the world outside the wrapper class unless it's made unmodifiable.

This is an easier way to create an internal "array" with some nice handling of values and without the extra memory burden of an Integer-keyed Map. Though, Arrays.asList(...) may still be just as fine for your purposes.

梦初启 2024-10-27 18:16:26

是的,是可能的:

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

现在,myArrayList 的初始容量将为 100

Yes is posible:

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

now, the initial capacity of myArrayList will be 100

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文