Implementing an Iterator for a Set, tracking the current element - java

How do you code an Iterator for a Set? Given that the iterator does not have access to the underlying data storage mechanism, and can only use the Set methods, is it possible to do this?
Every implementation I've managed to find creates the Iterator as an anonymous class; however, I am trying to figure out if there is a clever way to iterate over a Set while only accessing the methods provided by Set.
So far, the best I've managed to come up with looks like this:
import java.util.*;
public class SetIterator<E> implements Iterator
{
protected E[] arrayData;
protected Set<E> set;
protected int index;
protected boolean canRemove;
public SetIterator(Set<E> set)
{
this.set = set;
this.arrayData = (E[]) set.toArray();
this.index = -1;
this.canRemove = false;
}
public E next()
{
if(this.hasNext())
{
this.canRemove = true;
return this.arrayData[++this.index];
}
else
{
throw new NoSuchElementException("There is no next element");
}
}
public boolean hasNext()
{
return this.index + 1 < this.arrayData.length;
}
public void remove()
{
if(this.canRemove)
{
this.set.remove(this.arrayData[index--]);
this.arrayData = (E[]) this.set.toArray();
this.canRemove = false;
}
else
{
throw new IllegalStateException("Cannot remove element before calling next");
}
}
}
But that feels quite kludgy.... Is there a better way?

I think your title doesn't leave much space for answers, but if I use the following as your actual question:
How do you build an Iterator for a Set?
(and understand build as in get an instance of)
I think as PM 77-1 pointed out in the comments:
call the iterator() method on it, which it has since at least Java 1.5.
Keep in mind that it depends on the actual implementation of Set, wether the elements will always be iterated over in the same order.

if we look in AbstractCollection we will see that toArray actually calls the iterator() (abstract method) to produce the array which you will use, so your method still depends on the specific iterator, so you are essentially decorating the iterator.
public Object[] toArray() {
// Estimate size of array; be prepared to see more or fewer elements
Object[] r = new Object[size()];
Iterator<E> it = iterator();
for (int i = 0; i < r.length; i++) {
if (! it.hasNext()) // fewer elements than expected
return Arrays.copyOf(r, i);
r[i] = it.next();
}
return it.hasNext() ? finishToArray(r, it) : r;
}
Still not sure what you are trying to accomplish, the underlying datastructure of the set will have different (and specific) ways to efficently iterate the data, any generic solution would sacrafice performance, using the iterable interface should be generic enough.

Related

How to implement subList() in Custom ArrayList

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));
}
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,%20int)
Otherwise you will have to write a new class that properly delegates everything to the original list.
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).

Iterating over HashSet with for each

I'm trying to iterate over the Integer objects of a HashSet and I want to count the number of times an element occurs. this is my method so far
public int freq(int element) {
int numElements = 0;
for (int atPos : mySet){
if (mySet.atPos == element){ //says atPos cannot be resolved to a field
numElements++;
}
}
return numElements;
}
would it be better to use an iterator to iterate over the elements? How do I fix my
mySet.atPos
line?
This is where I initialize my HashSet
private HashSet <Integer> mySet = new HashSet<Integer>();
A Set cannot contain duplicate elements. Therefore you will always get a count of 0 or 1 for your element.
For any collection, you can get the frequency of the elements with:
public int freq(int element) {
return Collections.frequency(mySet, element);
}
Not sure you'd want to make a method out of it ...
Your issue is a simple misunderstanding of how you can use variables. int atPos and mySet.atPos do not refer to the same thing. The former refers to a local variable, the latter is looking for a public member of field of an instance of a set called the same thing.
You are trying to access this field:
public class HashSet
{
public int atPos; //<<<
}
but, when we think of it this way, obviously that field does not exist in HashSet!
All you need to do is get rid of mySet. and your code will work.
if (atPos == element){
numElements++;
}
Would it be better to use an iterator to iterate over the elements?
No, there's no benefit to using an iterator in this situation. A for each is more readable.
As others have noted, because sets will never contain duplicates, your numElements will actually only ever be one or zero. As such, you could actually write your function very compactly as:
public int freq(int element) {
if (myset.contains(element)) {
return 1;
}
else {
return 0;
}
}
Or even better using the ternary operator:
public int freq(int element) {
return myset.contains(element) ? 1 : 0;
}

Java Iterable object won't Iterate correctly

I have a java object that implements the Iterable interface. This linked list has a group of objects that each hold a parameterized object (so it could be a String OR an ArrayList or whatever).
In the iterator() method, i create an Iterator object that takes an array of these parameterized items and return it.
However, when i use the following code:
//create an Iterable object named 'iterate'
for(String current : iterate){
//try to do some stuff with it... print it out?
}
//more code
//here is how i implemented the iterator function of my Iterable class:
public Iterator<Item> iterator(){
// return an iterator over items in order from front to end
Item[] items = (Item[]) new Object[numberOfItems];
QueueItem<Item> item = first;
for(int i=0;i<numberOfItems;i++){
items[i] = item.getInfo();
item = item.next;
}
return new myIterator(items);
}
//here is the code for the myIterator class. it is a private internal class
private class myIterator implements Iterator<Item>{
Item items[];
int index;
public myIterator(Item current[]){
items = current;
index = 0;
}
#Override
public boolean hasNext() {
if(items==null){
return false;
}
return (index>=items.length);
}
#Override
public Item next() {
if(index+1>=items.length){
throw new NoSuchElementException("There are no more elements!");
}
return items[index++];
}
#Override
public void remove() {
throw new UnsupportedOperationException("this operation is not supported");
}
}
the code goes to the for loop, then creates the Iterator object correctly (I used breakpoints to confirm), and returns it, but then the code jumps out of the for loop without going through it even once and continues....
Am i missing something? did i do something wrong here? how do i need to change the implementation of my Iterable object? is there some GOTCHA with implementing this that i'm not taking into account?
thanks!
in hasNext method you check if return (index>=items.length); Well this will return false. change operant to "<"
oh my gosh. so embarrassing. the hasNext method had the boolean statement reversed.
it should have been:
return (index<items.length); thanks anyways!
i've been pulling my hair out about this. i debugged everything else, then it dawned on me.
This is my first iterator implementation, so i figured it must have been something more fundamental that i wasn't doing correctly.

Could anyone tell me why this returns an empty list (NPE)?

I have this code that is supposed to merge two instances of SortedLinkedList into one SLL (based on mergeSort merge), but is returning an empty list instead:
import java.util.LinkedList;
public class SortedLinkedList<T extends Comparable<? super T>>
extends LinkedList<T> {
private LinkedList<T> list; // the sorted list
// constructor, sorted with insertion sort
public SortedLinkedList(LinkedList<T> in)
{
if(in.peek() == null || in.size() == 1)
return;
else {
list = new LinkedList<T>();
for(T e : in)
list.add(e);
int i, j;
T temp;
for(i = 0; i < list.size(); i++){
j = i;
temp = list.get(j);
while(j > 0 && list.get(j-1).compareTo(temp) > 0){
list.set(j, list.get(j-1));
j--;
}
list.set(j, temp);
}
}
}
// return the union of the sorted linked lists this
// and other
public SortedLinkedList<T> makeUnion( SortedLinkedList<T> other)
{
list = new LinkedList<T>();
SortedLinkedList<T> temp = new SortedLinkedList<T>(list);
int i = 0, j = 0;
while(i < this.size() && j < other.size()){
if(this.get(i).compareTo(other.get(j)) <= 0){
temp.add(this.get(i));
i++;
}
else {
temp.add(other.get(j));
j++;
}
}
while(i < this.size()){
temp.add(this.get(i));
i++;
}
while(j < other.size()){
temp.add(other.get(j));
j++;
}
return temp;
}
// print the items in list
public void print()
{
for(T e : list)
System.out.println(e);
}
}
In the SLL constructor, I have it simply return on a null list (and the private variable, list, is initialized in the first line of this method). However from what I know, this should still give me an SLL object (initially also null). I can add to temp just fine in the method itself, but get a NullPointerException when printing the list.
I realize it's not very efficient to use get with LinkedList. I'll switch them with an iterator after I settle this.
Any hints would be quite appreciated.
EDIT: Interestingly, I get the same result if I put both lists in a temporary LL and then use the constructor on it. The types are compatible since SLL extends LL:
public SortedLinkedList<T> makeUnion( SortedLinkedList<T> other)
{
LinkedList<T> temp = new LinkedList<T>();
temp.addAll(this);
temp.addAll(other);
SortedLinkedList<T> merge = new SortedLinkedList(temp);
return merge;
}
EDIT2: It seems #Mead was correct... while size() and get() seem to work for the SLL, add() does not. I was thinking that since I'm extending LinkedList, it would work with the SLL as well. It didn't, and overriding them did nothing as well... I'm out of ideas for this. Suggestions?
Great! Your edit pretty much reveals your problem: you're not extending the LinkedList properly. Fix that, and then work on union.
The problem at hand: This is a class called SortedLinkedList. We can assume it's meant to be just like LinkedList, but the values in it are sorted. So, given that, this should work:
LinkedList<Integer> unsorted = new LinkedList<Integer>();
unsorted.add(200);
unsorted.add(100);
unsorted.add(300);
SortedLinkedList<Integer> sorted = new SortedLinkedList<Integer>(unsorted);
System.out.println(sorted.size());
for (Integer i : sorted) {
System.out.println(i);
}
// Should print out:
// 3
// 100
// 200
// 300
But it will not. Run your code, what does it print out?
Back? Why did it print out that? First, consider two variables you can use in the class's code: this refers to the SortedLinkedList object, and this.list refers to an instance variable inside that SortedLinkedList object. Then let's look at the constructor: when you add to the list, you're calling this.list.add(). What you have written makes SortedLinkedList a wrapper around the list instance variable - you're not adding to the SortedLinkedList (this) you are adding to a list inside that (this.list).
The only methods that use your this.list instance variable are the constructor, print, and makeUnion. All the other LinkedList methods aren't aware of the list variable, so when I call get():
LinkedList<Integer> unsorted = new LinkedList<Integer>();
unsorted.add(200);
unsorted.add(100);
unsorted.add(300);
SortedLinkedList<Integer> sorted = new SortedLinked<Integer>(unsorted);
System.out.println(sorted.get(0));
It doesn't know to look in your this.list variable, so it won't get 100 to print. In fact, it will crash because there is no value in index 0. You didn't add to the instance variables that get() actually uses, so the methods think that SortedLinkedList object is empty. this.list is a new variable that the inheritted methods don't know about.
So, if we examine your latest edit:
public SortedLinkedList<T> makeUnion( SortedLinkedList<T> other)
{
LinkedList<T> temp = new LinkedList<T>();
temp.addAll(this);
temp.addAll(other);
SortedLinkedList<T> merge = new SortedLinkedList(temp);
return merge;
}
temp.addAll(this) doesn't work, because all the methods of this think that the list is empty because they're not looking at this.list. temp.addAll(other) doesn't work either, for the same reason.
What is common when you extend classes is that you want the existing methods to continue working. This means that you need to store the data where get() and other methods expect it to be. How do you do that? Well, you're already doing it! You are already doing the right thing - but you are doing it on the instance variable this.list instead of this. Start calling this.add(), this.set(), this.size() instead of this.list.add() and remove the instance variable list completely - it's not needed, you have this. Then the data will be where the other methods expect it to be.
(And call super() on the first line of your constructor, so the code in the super class's constructor is called). Good luck on your homework - I'd recommend testing the object works as-is before adding new methods.
just curious after looking at the implementation, but couldn't you have just done a .addAll(...) followed by a Collections.sort(...)? That's what I would have preferred personally.

LinkedList.contains execution speed

Why Methode LinkedList.contains() runs quickly than such implementation:
for (String s : list)
if (s.equals(element))
return true;
return false;
I don't see great difference between this to implementations(i consider that search objects aren't nulls), same iterator and equals operation
Let's have a look at the source code (OpenJDK version) of java.util.LinkedList
public boolean contains(Object o) {
return indexOf(o) != -1;
}
public int indexOf(Object o) {
int index = 0;
if (o==null) {
/* snipped */
} else {
for (Entry e = header.next; e != header; e = e.next) {
if (o.equals(e.element))
return index;
index++;
}
}
return -1;
}
As you can see, this is a linear search, just like the for-each solution, so it's NOT asymptotically faster. It'd be interesting to see how your numbers grow with longer lists, but it's likely to be a constant factor slower.
The reason for that would be that this indexOf works on the internal structure, using direct field access to iterate, as opposed to the for-each which uses an Iterator<E>, whose methods must also additionally check for things like ConcurrentModificationException etc.
Going back to the source, you will find that the E next() method returned by the Iterator<E> of a LinkedList is the following:
private class ListItr implements ListIterator<E> {
//...
public E next() {
checkForComodification();
if (nextIndex == size)
throw new NoSuchElementException();
lastReturned = next;
next = next.next;
nextIndex++;
return lastReturned.element;
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
This is considerably "busier" than the e = e.next; in LinkedList.contains! The iterator() of a LinkedList is actually a ListIterator, which has richer features. They aren't needed in your for-each loop, but unfortunately you have to pay for them anyway. Not to mention all those defensive checks for ConcurrentModificationException must be performed, even if there isn't going to be any modification to the list while you're iterating it.
Conclusion
So yes, iterating a LinkedList as a client using a for-each (or more straightforwardly, using its iterator()/listIterator()) is more expensive than what the LinkedList itself can do internally. This is to be expected, which is why contains is provided in the first place.
Working internally gives LinkedList tremendous advantage because:
It can cut corners in defensive checks since it knows that it's not violating any invariants
It can take shortcuts and work with its internal representations
So what can you learn from this? Familiarize yourself with the API! See what functionalities are already provided; they're likely to be faster than if you've had to duplicate them as a client.
I decided to test this and came out with some interesting result
import java.util.LinkedList;
public class Contains {
private LinkedList<String> items = new LinkedList<String>();
public Contains(){
this.addToList();
}
private void addToList(){
for(int i=0; i<2000; i++){
this.items.add("ItemNumber" + i);
}
}
public boolean forEachLoop(String searchFor){
for(String item : items){
if(item.equals(searchFor))
return true;
}
return false;
}
public boolean containsMethod(String searchFor){
if(items.contains(searchFor))
return true;
return false;
}
}
and a JUnit testcase:
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class ContainsTest {
#Test
public void testForEachLoop(){
Contains c = new Contains();
boolean result = c.forEachLoop("ItemNumber1758");
assertEquals("Bug!!", true, result);
}
#Test
public void testContainsMethod(){
Contains c = new Contains();
boolean result = c.containsMethod("ItemNumber1758");
assertEquals("Bug!!", true, result);
}
}
This funny thing is when I run the JUnit test the results are :
- testForEachLoop() - 0.014s
- testContainsMethod() - 0.025s
Is this true or I am doing something wrong ?

Categories