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.
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
Iterable<Position<Integer>> iterable = list.positions();
Iterator<Position<Integer>> iter = iterable.iterator();
while (iter.hasNext()) {
System.out.println(iter.next().getData());
}
The above code works with no issues. list is just an instance of a List class that I wrote. It contains elements of the Integer type.
for (Position<Integer> pos : iterable) {
}
This code fails at the part past the colon. This should be equivalent to the first piece of code, the one with the while loop. So I don't understand why the for-each loop has an error. The error says: "Can only iterate over an array or an instance of java.lang.Iterable" - but iterable already is Iterable, isn't it? What am I missing here?
the following is the full code implementing the aforementioned methods and types.
private class PositionIterator implements Iterator<Position<E>> {
private Position<E> cursor = first();
private Position<E> current = null;
public boolean hasNext() {
return cursor.getData() != null;
}
public Position<E> next() {
if (cursor == null) throw new NoSuchElementException("reached the end of the list");
current = cursor;
cursor = after(cursor);
return current;
}
}
private class PositionIterable implements Iterable<Position<E>> {
public Iterator<Position<E>> iterator() {
return new PositionIterator();
}
}
public Iterable<Position<E>> positions() {
return new PositionIterable();
}
these are nested classes within another class called PositionalList<E>. In the interest of keeping this post compact, I decided to omit the outside class. It's just a bunch of getter and setter methods that are typical for a List class.
public interface Iterable<E> {
public Iterator<E> iterator();
}
^that's the Iterable interface being implemented by PositionIterable
public interface Iterator<E> {
boolean hasNext();
E next();
}
^And that's the Iterator interface.
The enhanced for loop accepts an Iterable, not an Iterator. iter is an Iterator.
Therefore :
for (Position<Integer> pos : iter)
Should be :
for (Position<Integer> pos : iterable)
EDIT : Based on the comments, your problem must be hiding java.lang.Iterable by your custom Iterable interface. If your iterable variable is of the type of your custom Iterable interface, it can't be used by the enhanced for loop, which accepts java.lang.Iterable.
You shouldn't have any issues running that code. Here's my local test code
public static void main(String[] args)
{
Iterable<String> iterable = Arrays.asList("foo",
"bar");
for (String anIterable : iterable)
{
System.out.println(anIterable);
}
}
If you have created a local class or interface called Iterable, that's the only reason I could think why this wouldn't work. If you have done that, delete it and then maybe go back and review the purpose of interfaces too.
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.
private SomeObject[] all_objs;
public Iterator<SomeObject> iterator() {
//
}
What is the best way to get an iterator from an array of SomeObject?
EDIT
So there is no way to generate iterator without using the wrappers like ArrayLists or HashSets ?
You could write your own class that implements Iterator<SomeObject>. Simply implement the methods of the interface. If you don't want the users of the iterator from removing items from the array (since it isn't an ArrayList), then throw an UnsupportedOperationException if this method is called.
Of course if you use an ArrayList<SomeObject> then the iterator() method would only have to return the iterator from the ArrayList making your life much easier.
Using the List iterator:
import java.util.List;
import java.util.Arrays;
public <T> Iterator<T> iterator() {
List<T> list = (List<T>) Arrays.asList(all_objs);
return list.iterator();
}
You can also look at the code of java.util.ArrayList. Here is how you should implement it.
T=SomeObject //Your Class
public YourIterable<T> implements Iterable<T>{
//**Iterable** Implementing this interface allows an object
//to be the target of the "foreach" statement.
public Iterator<T> iterator() {
return new Itr();
}
private class Itr implement Iterator<T> {
//Complex Implementation - You have to maintain a Cursor to keep track of
// which record was accessed
boolean hasNext() {
//Returns true if the iteration has more elements.
}
T next() {
//Returns the next element in the iteration.
}
void remove() {//Remove
}
}
}
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.