I was looking at the http://docs.oracle.com/javase/tutorial/collections/custom-implementations/index.html tutorial and I tried to do the same :
class MyArrayList<T> extends AbstractList<T> {
private final T[] a;
MyArrayList(T[] array) {
a = array;
}
#Override
public T get(int index) {
return a[index];
}
#Override
public T set(int index, T element) {
T oldValue = a[index];
a[index] = element;
return oldValue;
}
#Override
public int size() {
return a.length;
}
#Override
public Object[] toArray() {
return (Object[]) a.clone();
}
public static void main(String[] args) {
String[] arr = {"one", "two", "three"};
MyArrayList<String> list = new MyArrayList<String>(arr);
list.get(1);
list.add(1, "seven");
System.out.println(list);
}
}
I get an exception while trying to insert the element :
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.add(Unknown Source)
Why is that, how do I fix it?
You are not overriding the method add().
The javadoc for AbstractList states:
Note that this implementation throws an UnsupportedOperationException
unless add(int, Object) is overridden.
The fix is... to override the method. Or not use the add() method so your MyArrayList's size is immutable (but not it's values) - like an array, which is what you're storing your values in.
From the java documentation of Abstract List:
"To implement a modifiable list, the programmer must additionally override the set(int, E) method (which otherwise throws an UnsupportedOperationException). If the list is variable-size the programmer must additionally override the add(int, E) and remove(int) methods."
You have to override the add method :)
You need to implement add method if you want a mutable list.
add is optional on list. AbstractList implements it to throw and exception. If you want it to do something else, then you can simply override it.
First of all, do you really need to implement an abstract list? In most of the cases, java List is enough for you.
Annoying part of implementing an abstract class is you have to implement every methods which throws UnsupportedOperationException.
Related
I am trying to create class that extends the java.util.ArrayList by overriding
the add method in the following way: it does nothing if the object to be added already exists
in the collection; otherwise it calls the add method in the super class to add the object into
the collection.
My code look like that so far but it gives a NullPointerException:
import java.util.ArrayList;
public class myArrayList<E> extends ArrayList<E> {
public ArrayList<E> mylist;
#Override
public boolean add(E e) {
if (!mylist.contains(e)) {
super.add(e);
return true;
} else {
return false;
}
}
}
public static void main(String[] args) {
myArrayList<Integer> listing = new myArrayList<Integer>();
listing.add(4);
listing.add(4);
for (int i = 0; i < listing.size(); i++) {
System.out.println(listing.get(i));
}
}
While we can't be sure this is your problem (unless you show us the stacktrace!), it looks like an NPE is likely to occur in this line:
if (!mylist.contains(e)) {
because mylist is never initialized.
In fact, if you are trying to extend ArrayList rather than create a list wrapper, the mylist variable should not exist at all. The list state is in the superclasses private variables. Instead, the add method should probably be written like this:
#Override
public boolean add(E e) {
if (!super.contains(e)) {
return super.add(e); // See note
} else {
return false;
}
}
Note: in general, you should return whatever the superclass returns here. However, when the superclass is ArrayList, we know1 that add will return true, so this is only "saving" a line of code. There might be marginal performance difference, depending on how smart the JIT optimizer is.
1 - We know because the ArrayList.add javadoc specifically states that true is returned.
But this looks like you are trying to create a "set-like" list class. There could be better alternatives; e.g. LinkedHashSet has a defined iteration order, and addition is O(1) rather than O(N). (Admittedly, LinkedHashSet uses a lot more memory, and it has no efficient equivalent to the positional List.get(int) method.)
You got a NullPointerException on this line if (!mylist.contains(e)) { because myList is not instanciated in the default constructor.
public MyArrayList() {
this.myList = new ArrayList<>();
}
But.. you mix inheritance and composition here...
That means add will be applied to myList and get(index) will be applied on this.. So you actually maintain 2 lists here..
In you example myList.contains will always return false because you never add something into. this -> super.add(e) is the same than this.add(e) and it is a different instance of list.
So just removed myList instance field and replace your add like this :
#Override
public boolean add(E e) {
if (!contains(e)) {
add(e);
return true;
} else {
return false;
}
}
Watch out that this class is not thread-safe. Here there is a check-then-act race condition (check = contains(), act = add())
Finally List are designed to allow duplicates... if you don't want duplicates.. just use a Set
I'm Trying to modify Java Vector to raise size if im accessing an element bigger than the vector's size. And to insert a new element if im accessing a not initialized element.
Eclipse throws cannot instantiate the type Obj.
public static class Vec<Obj> extends Vector<Obj> {
#Override
public Obj set(int a, Obj b) {
if (super.size()<=a) super.setSize(a+1);
return (Obj) super.set(a,b);
}
#Override
public Obj get(int a) {
if (super.size()<=a) super.setSize(a+1);
if (super.get(a)==null) super.insertElementAt( new Obj() , a);
return (Obj) super.get(a);
}
public Vec () {
super();
}
}
There is no guarantee that T has a no-args constructor. Also, people like to use interfaces, so there's a good chance T wont be concrete.
So, supply an abstract factory to the construction of your Vec. A suitable type is java.util.function.Supplier<T>.
private final Supplier<T> dflt;
public Vec(Supplier<T> dflt) {
super();
this.dflt = Objectes.requireNonNull(dflt);
}
...
if (super.get(a)==null) {
super.insertElementAt(dflt.get(), a);
}
Construct as:
Vec<Donkey> donkeys = new Vec<>(BigDonkey::new);
java.util.Vector methods should be synchronized, although such locking isn't really useful and ArrayList should generally be used instead. Even then, subclassing like this breaks LSP.
I have many instance in code where a Collection, most usually a List is returned from a method and to account for the caller possibly altering that list, a defensive copy is made.
The JDK does not seem to provide a wrapper for this purpose, so I'm trying to roll my own (Note: java.util.concurrent.CopyOnWriteArrayList is not what I'm looking for here).
Schematically, what I'm trying is:
public class CopyOnWriteList<E> extends List<E> {
protected List<E> list;
protected boolean isCopied;
public CopyOnWriteList(List<E> list) {
this.list = list;
}
private void ensureCopy() {
if (!isCopied) {
list = new ArrayList<E>(list);
isCopied = true;
}
}
public E get(int i) {
return list.get(i);
}
public boolean add(E e) {
ensureCopy();
return list.add(e);
}
// ... many more simple methods
}
For most of the API this is simple enough, but looking at java.util.ArrayList I found, that for the method iterator() it returns a different implementation than for listIterator(), although is ListIterator extends its Iterator and it doesn't add any new members.
I wonder if there is a deeper reason behind this - I planned simply to implement iterator() and listIterator() all returning the same type of iterator - a ListIterator. Is there any logical reason why one should not return a ListIterator from the iterator()-method?
ListIterator extends Iterator. So if you return an object that implements ListIterator then it obviously also implements Iterator. But you also can decide to return different object that only implements Iterator, because the implementation could be more effective. The decision is up to you.
Why do i get the following compile error:
LRIterator is not abstract and does not override abstract method remove() in java.util.Iterator
Note, the implementation is for a linked list
public Iterator iterator()
{
return new LRIterator() ;
}
private class LRIterator implements Iterator
{
private DLLNode place ;
private LRIterator()
{
place = first ;
}
public boolean hasNext()
{
return (place != null) ;
}
public Object next()
{
if (place == null) throw new NoSuchElementException();
return place.elem ;
place = place.succ ;
}
}
Java 8
In Java 8 the remove method has a default implementation that throws UnsupportedOperatorException so in Java 8 the code compiles fine.
Java 7 and below
Because the Iterator interface has a method called remove(), which you must implement in order to say that you have implemented the Iterator interface.
If you don't implement it the class is "missing" a method implementation, which is only okay for abstract classes, i.e., classes that defer implementation of some methods to subclasses.
The documentation may seem confusing as it says that remove() is an "optional operation". This only means that you don't have to actually be able to remove elements from the underlying implementation, but you still need to implement the method. If you don't want to do actually remove anything from the underlying collection you implement it like this:
public void remove() {
throw new UnsupportedOperationException();
}
You must implement remove because it is part of the contract defined by the Iterator interface.
If you don't want to implement it then make it throw an exception instead:
public void remove() {
throw new UnsupportedOperationException();
}
...because you're not providing a definition for remove(), and Iterator is an interface, so you must supply a definition for all of its functions for any concrete implementation.
You can, however, add a method that throws an exception if you don't want to support the functionality:
public void remove(){
throw new UnsupportedOperationException();
}
Iterator is an interface which means that you should implement all methods
public interface Iterator<E> {
boolean hasNext();
E next();
void remove();
}
hasNext() and next() you already have so just add remove() method
If you haven't any idea just throw appropriate exception:
public void remove() {
throw new UnsupportedOperationException();
}
You have to add a
public Object remove() {
throw new RuntimeException ("I'm not going to implement it!!!");
}
Given a generic class:
class MyClass (
private List l = new LinkedList <String>();
public void addElement (String s) (l.add (s);)
.............
)
an accessor method that allows me to iterate on the list as it should be?
I had decided to implement a method that returns an iterator directly, but does not seem correct because it could change the list from the outside with remove ().
What do you think?
import java.util.*;
public Iterator<String> elements() {
return Collections.unmodifiableList(elements).iterator();
}
If you don't mind exposing the fact that the elements are stored as a List, you could also use do:
public ListIterator<String> elements() {
return Collections.unmodifiableList(elements).listIterator();
}
If you want to allow callers to use the "foreach" syntax, you might want to return an Iterable:
public Iterable<String> getElements() {
return Collections.unmodifiableList(elements);
}
And, again, if you don't mind exposing that the elements are returned as a List, this last solution could return List<String>
Most common way would be to implement Iterable interface but since you don't want to expose remove method you can follow the advice from NamshubWriter or provide your implementation of get(index) and size() methods (assuming your class should behave like a List). This will allow index based iteration.
however such a thing would be fine?
public Iterator<String> getList(){
return new Iterator<String>(){
Iterator<String> i=l.iterator();
public boolean hasNext() {
return i.hasNext();
}
public String next() {
if(!i.hasNext()) throw new NoSuchElementException();
return i.next();
}
public void remove() {
throw new UnsupportedOperationException();
}
}
}
need a method that let me just browse the collection elements and preserve encapsulation, method unmodifiable ... I know but I can not use it.