Reputation: 3
i have the following problem. I've been doing a task where the iterator is given and Predicate class have to check, if the String exists in the iterator. I overrode hasNext()
and next()
methods from Iterator. Hier is the PredicateIteratorTest. I get the message junit.framework.AssertionFailedError: expected:<Java [and UML]> but was:<Java [11]> at PredicateIteratorTest.providesValuesBeginningWithJava(PredicateIteratorTest.java:37)
It should return the previous element.
public class PredicateIteratorTest {
private final List<String> values = List.of(//
"Java and UML", "UML and Java", "Java 11", "UML 2.0", "Effective Java");
private Iterator<String> valuesEndingWithJava;
private Iterator<String> valuesBeginningWithJava;
private Iterator<String> noValues;
@Before
public void setUp() {
valuesEndingWithJava = new PredicateIterator<>(values.iterator(), new EndsWith("Java"));
valuesBeginningWithJava = new PredicateIterator<>(values.iterator(), new StartsWith("Java"));
noValues = new PredicateIterator<>(values.iterator(), new StartsWith("Doesn't match"));
}
@Test
public void providesValuesEndingWithJava() {
assertTrue(valuesEndingWithJava.hasNext());
assertEquals("UML and Java", valuesEndingWithJava.next());
assertTrue(valuesEndingWithJava.hasNext());
assertEquals("Effective Java", valuesEndingWithJava.next());
}
}
Here's the PredicateIterator class. After if(predicate.test(iterator.next())){
i want to do something simmilar to method iterator.previous()
from ListIterator, but without using ListIterator, because tests use only Iterator. How can i make it ?
public class PredicateIterator<T> implements Iterator<T>{
private Iterator<T> iterator;
private Predicate<T> predicate;
public PredicateIterator(Iterator<T> iter, Predicate<T> predicate){
this.iterator = iter;
this.predicate = predicate;
}
@Override
public boolean hasNext(){
while(iterator.hasNext()){
if(predicate.test(iterator.next())){
return true;
}
}
return false;
}
@Override
public T next(){
T elem;
while (iterator.hasNext()){
elem = iterator.next();
if(predicate.test(elem)){
return elem;
}
}
throw new NoSuchElementException();
}
}
I've changed PredicateIterator by adding ListIterator and then iterator.previous(). But now i have test error java.lang.UnsupportedOperationException at java.base/java.util.ImmutableCollections.uoe(ImmutableCollections.java:73) at java.base/java.util.ImmutableCollections$ListItr.previous(ImmutableCollections.java:260) at PredicateIterator.hasNext(PredicateIterator.java:20) at PredicateIteratorTest.providesValuesBeginningWithJava(PredicateIteratorTest.java:36)
private ListIterator<T> iterator;
private Predicate<T> predicate;
public PredicateIterator(Iterator<T> iter, Predicate<T> predicate){
this.iterator = (ListIterator<T>) iter;
this.predicate = predicate;
}
@Override
public boolean hasNext(){
while(iterator.hasNext()){
if(predicate.test(iterator.next())){
iterator.previous();
return true;
}
}
return false;
}
Upvotes: 0
Views: 1186
Reputation: 26
create a boolean field and assign it to false but it in the the hasNext method after finding a value assign it to true and then check this field in next method, if it was false that's mean you called the next method before the hasNext method, then you have to call hasNext method in the next method.
public class PredicateIterator<T> implements Iterator<T> {
private Predicate predicate;
private Iterator<T> iter;
private T type;
private boolean test = false;
public PredicateIterator(Iterator<T> iter, Predicate<T> predicate) {
if (iter == null || predicate == null) throw new NoSuchElementException();
this.predicate = predicate;
this.iter = iter;
}
@Override
public boolean hasNext() {
if (iter == null) throw new NoSuchElementException();
while (iter.hasNext()) {
type = iter.next();
if (predicate.test(type)) {
test = true;
return true;
}
}
return false;
}
@Override
public T next() {
if (!test) {
if ( hasNext()) {
T newTyp = type;
type = null;
test = false;
return newTyp;
}
}
else if (type != null) {
T newTyp = type;
type = null;
test = false;
return newTyp;
}
throw new NoSuchElementException();
}
}
Upvotes: 1
Reputation: 40034
Ok, I found the problem. I modified your PredicateIterator<T>
class to save the value before doing the predicate testing. It seems to work with the tests defined.
class PredicateIterator<T> implements Iterator<T> {
private Iterator<T> iterator;
private Predicate<T> predicate;
T value;
public PredicateIterator(Iterator<T> iter,
Predicate<T> predicate) {
this.iterator = iter;
this.predicate = predicate;
}
@Override
public boolean hasNext() {
while (iterator.hasNext()) {
value = iterator.next(); //<-- save value before test
if (predicate.test(value)) {
return true;
}
}
return false;
}
@Override
public T next() {
if (value != null) {
T retValue = value;
value = null;
return retValue; // <-- return saved value.
} else {
throw new NoSuchElementException();
}
}
}
One more observation. If you need to alter any of the test list you won't be able to since List.of()
returns an immutable list. But you can pass that as a an argument to a mutable implementation. E.g. new ArrayList<>(List.of(...));
Upvotes: 0
Reputation: 3
Thank you very much, i copied your hasNext()
methode and changed next()
, so it looks like this:
@Override
public T next() {
if(value!= null){
if(predicate.test(value)) {
return value;
}
}
throw new NoSuchElementException();
}
First test providesValuesEndingWithJava()
compiles now correctly, but now the other problem appeared. It throws an NoSuchElementException
. Here's the test and the message i got. I tried to make with while(value!=null)
or while(iterator.hasNext())
, but then it compiles too long and crashes.
@Test
public void hasNextReturnsFalseAfterLastElement1() {
valuesEndingWithJava.next();
valuesEndingWithJava.next();
assertFalse(valuesEndingWithJava.hasNext());
}
java.util.NoSuchElementException
at PredicateIterator.next(PredicateIterator.java:34)
at PredicateIteratorTest.hasNextReturnsFalseAfterLastElement1(PredicateIteratorTest.java:44)
Upvotes: 0