I'm implementing my own generic ArrayList and everything is fine, but I've got a problem with the last method - List subList(int fromIndex, int toIndex). I've tried to implement it, but still, I only create a copy of my main list and modify only it. My current code is
public List<T> subList(int fromIndex, int toIndex) {
if (fromIndex < 0 || toIndex > size() || fromIndex > toIndex)
throw new IndexOutOfBoundsException();
Object subarray[] = new Object[toIndex - fromIndex];
for (int i = 0; i < subarray.length; i ) {
subarray[i] = array[fromIndex i];
}
List<T> subList = Arrays.asList((T) subarray);
return subList;
}
I've also tried to do it using ArrayList instead of an array, but it still doesn't work (modify only my new subList instead of the whole list). How can I repair it? Do I have to implement another class?
EDIT I also add my test method, I still get failure (the value in main list isn't changed)
@Test
void subListWithoutNulls() {
CustomArrayList<Integer> array = new CustomArrayList<>();
array.add(3);
array.add(7);
array.add(2);
array.add(5);
array.add(8);
array.add(3);
array.subList(1, 4).set(0, 12);
assertEquals(12, array.get(1));
}
CodePudding user response:
If you derive from AbstractList
, an implementation will be provided for you: https://docs.oracle.com/javase/7/docs/api/java/util/AbstractList.html#subList(int, int)
Otherwise you will have to write a new class that properly delegates everything to the original list.
CodePudding user response:
Assuming you are doing this is a learning exercise, here's one way to create a sublist that has pass-through behaviour. It involves making an implementation of the List
interface. Just about every method implementation is specifically to cater to the idea of a sublist. As the specs say, you don't have to consider the case of structural changes made to the underlying list (e.g. what if in between making a subList and e.g. calling size() on that subList, someone invokes .clear()
on the top-level list? Does that mean the sublist should now return 0? You get to define what happens yourself, as per the spec of java.util.List
.
public List<T> subList(int fromIndex, int toIndex) {
if (fromIndex < 0) throw new IndexOutOfBoundsException("fromIndex is below 0");
if (toIndex > size()) throw new IndexOutOfBoundsException("toIndex is above 'size'");
if (toIndex < fromIndex) throw new IndexOutOfBoundsException("toIndex is before fromIndex");
return new List<T>() {
public void size() {
return toIndex - fromIndex;
}
public void get(int idx) {
return MyList.this.get(idx fromIndex);
}
public boolean add(T elem) {
return MyList.this.add(toIndex , elem);
}
// and so on.
};
}
Take a look at that impl of the add
method (which makes the assumption that the underlying add
method always returns true - if your list impl doesn't, do not increment toIndex unless true is returned): It can be a tad complicated. Here, adding an item to the subList means the subList is now one larger than it was. For that matter, so is the underlying list. Also an add-to-the-end operation on a subList doesn't neccesarily mean that you're adding to the end of the underlying list, so you'd invoke the add-in-the-middle version. Outer.this.method()
is java-ese for invoking a method from your outer class, which we need here, as both the inner class and the outer class have very similar methods, so we need to be clear when we invoke e.g. add(idx, elem)
- is that invoking the subList's add method, or the underlying (outer class's) add method? Outer.this.x
can be used to make that clear (if you don't do that, you get the inner list, and thus most likely a boatload of StackOverflowErrors if you try it).