Reputation: 45
I'm trying to figure out how I can get an output of remaining slots available when 1 object is removed.
ListOfMembers = new ArrayList<>(100); which caps my list at 100.
Whenever I delete 1 from the list, I need to print out the remaining space in the ArrayList.
public boolean DeleteMember() {
Scanner in = new Scanner(System.in);
System.out.println("Enter the membership number: ");
String pno = in. nextLine();
for(int i=0;i<ListOfMembers.size();++i) {
if(ListOfMembers.get(i).getMembershipNumber().equals(pno)) {
ListOfMembers.remove(i);
System.out.println("Space available: " +?????);
return true;
}
}
System.out.println("Numbership number does not exist");
return false;
}
Using System.out.println("Space available: " +ListOfMembers.size())
will provide the count of entries and I'm trying to have the opposite.
Upvotes: 4
Views: 538
Reputation: 40034
Capacity is an internal metric that is used to dynamically increase the available space used by the ArrayList<>()
Usually, it is of no consequence to the user how this is managed since it is an internal issue.
However, being able to set the capacity allows the user to specify a value indicative of the initial contents that the list will hold. The advantage is that it allows the list to fill the backing array without having to continually readjust the capacity resulting in a lot of copying of objects.
At any time you can also increase the capacity by using the ensureCapacity()
method.
And you can also release unused capacity by using trimToSize()
method. This may free up memory within the JVM.
But none of the above will prevent you from adding addtional entries to the list. They simply allow one to make optimum choices based on a priori knowledge of the data.
Upvotes: 1
Reputation: 140309
Reading the specification of List, it says that implementations can simply not implement add
; or refuse to add elements based on type, or some property of the element; but it doesn't say that a list can refuse to add an element based on the list's current size. Placing a cap on the size thus violates Liskov Substitutability.
You can define a LimitedSizeList implements Collection
, but it can't be a true implementation of java.util.List
.
You can easily implement LimitedSizeList
by extending AbstractCollection
:
class LimitedSizeList<E> extends AbstractCollection<E> {
private final List<E> list = new ArrayList<>();
private final int capacity;
LimitedSizeList(int capacity) {
this.capacity = capacity;
}
// Fill in the methods described in the Javadoc:
@Override
public Iterator<E> iterator() { return list.iterator(); }
@Override
public int size() { return list.size(); }
@Override
public boolean add(E element) {
// Collection.add does allow you to throw an IllegalStateException
// https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html#add-E-
if (remainingCapacity() <= 0) throw new IllegalStateException("Full");
return list.add(element)
}
// You don't have to, but you might want to consider overriding
// addAll, in order to make trying to add too-large a collection
// failure atomic (that is, it fails to add any rather than some).
// And then provide a method to report the free capacity:
int remainingCapacity() {
return capacity - size();
}
}
This is a far cleaner way to approach the problem than attempting to extend ArrayList
(not just because of the contract violation, but also for all the reasons to prefer composition over inheritance).
Of course, if you really want it to be an invalid List
(or you can guarantee that it won't need to be treated as a general-purpose List
), you can instead extend AbstractList
: the methods you need to implement are different, but there are relatively few, and they're quite easy. However, violating contracts is a good way to get surprising bugs in surprising places in your code.
Upvotes: 1
Reputation: 4859
You can use reflection to achieve this task to get the capacity first, then subtract the actual size
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
list.add("h");
try {
Field field = list.getClass().getDeclaredField("elementData");
field.setAccessible(true);
int cap = Array.getLength(field.get(list));
System.out.println("The capacity: " + cap);
System.out.println("The size: " + list.size());
System.out.println("The remaining capacity: " + (cap - list.size()));
} catch (Exception e) {
e.printStackTrace();
}
}
, output
The capacity: 10
The size: 6
The remaining capacity: 4
Upvotes: 0
Reputation: 102820
You seem to misunderstand how arraylist works.
new ArrayList<>(100)
does not cap the list. The 100 is merely a hint.
ArrayList is defined as allowing infinite* growth, and it has no facilities to limit how many items can be in them.
ArrayList works 'under the hood' by having an array that holds your elements. The problem is, java does not allow arrays to grow or shrink. ArrayList solves this problem with two tricks:
The only thing that 100
does in your constructor is serve as a hint: How large should the backing array be made initially. Out of the box (just new ArrayList<>()
), you get the default hint of 10.
Try it! run this:
List<String> list = new ArrayList<String>(100);
for (int i = 0; i < 200; i++) list.add("" + i);
System.out.println(list.size());
That code will compile fine, run fine, and print 200. Thus proving that the '100' has absolutely nothing to do with capping the size of this list.
So, how DO you cap a size of an arraylist?
You don't. Arraylist cannot do that. Instead, you wrap, or extend. For serious code bases, I strongly recommend wrapping, but for a simple exercise, extending can make your code a little shorter:
public class LimitedList<T> extends ArrayList<T> {
private final int limit;
public LimitedList(int limit) {
this.limit = limit;
}
@Override public boolean add(T in) {
if (size() >= limit) throw new IllegalStateException("List is full");
super.add(in);
}
@Override public boolean addAll(Collection<T> in) {
// left as an exercise for the reader
}
@Override public boolean addAll(int index, Collection<T> in) {
// left as an exercise for the reader
}
@Override public boolean add(int idx, T in) {
// left as an exercise for the reader
}
@Override public List<T> subList(int from, int to) {
// this one gets complicated!
// let's just not allow it.
throw new UnsupportedOperationException();
}
public int available() {
return limit - size();
}
}
NB: As you can see, you have to be very careful and override every method that may grow the list; this is why making a new type that doesn't extend ArrayList at all, and instead has 1 field of type ArrayList (and 1 field of int for the limit, of course), can be better: Now you explicitly have to think about every method list has, instead of praying you covered all the ones that add things.
*) well, pragmatically speaking, you can't have more than 2^31-1 elements.
Upvotes: 4