Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible for a method in Java 8 to determine simply whether an Iterable<Integer> Object provides a PrimitiveIterator.OfInt iterator?

Tags:

This is my first activity here, I hope I behave correctly! My problem: I am trying to write a collection of functions (static methods in a class) that accept one or several Iterable<Integer> and obtains the primitiveIterator.ofInt, providing it to another function that constructs yet another iterator (i.e. the functions are meant to compose iterators in various ways).

Here is a simple example of what I have in mind:

public static FilterIteratorInt filtor(Iterable<Integer> iter, IntPredicate filter)
{
    return new FilterIteratorInt((PrimitiveIterator.OfInt)iter.iterator(),filter);
}   

To me, it is not quite clear whether this can work at all, because the iterator returned by the Iterable<Integer> Object might not be of type PrimitiveIterator.OfInt.

In order to overcome this potential difficulty, I have been looking for a solution, which might either be an Interface like e.g. PrimitiveIterable.OfInt or any other way to find out whether the iterator is in fact primitive. I have been searching for quite a while, but while usually just browsing answers nearly all of my question, this time I had to register here to ask that question directly.

This construction is meant to avoid boxing/unboxing orgies, since I want the new iterators to be reasonably fast.

So here are three questions:

  1. Is there a way to find out whether the iterator obtained from an iterable is in fact a primitive iterator (such that the function can distinguish and act accordingly) or is there another way to obtain one?
  2. Is it possibly useless to try to improve performance this way? I.e. will (JIT or Java)compilers optimize this anyway, or is boxing/unboxing unavoidable anyway under the hood? Here I hope to learn something.
  3. Can somebody show me a different and better solution that could serve the same purpose (i.e. composition of primitive iterators, or of any iterators, while we are at it)?

Update: Due to Holgers answer, it boils down to the following question nr. 4: If next() is called on a PrimitiveInteger.OfInt, will this invoke the nextInt() method, or in other words: Will this automatically end up returning a pure int? Or will it still cause a boxing and unboxing sequence?

From the answer below I assume the latter, and this would mean it is definitely better to deal with nextInt() explicitly.

Assuming that this is correct (please tell me if I am wrong), I have used the instanceof method below and explicitly wrap if required.

like image 686
BlondMammuth Avatar asked Sep 12 '17 10:09

BlondMammuth


People also ask

Which methods are part of the iterator interface?

Iterator interface provides the following methods: boolean hasNext() - Returns true if the iteration has more elements. E next() - Returns the next element in the iteration. void remove() - Removes from the underlying collection the last element returned by the iterator (optional operation).

What does iterator hasNext do?

hasNext. Returns true if the iteration has more elements. (In other words, returns true if next() would return an element rather than throwing an exception.)


1 Answers

Well, you could simply use iterator instanceof PrimitiveIterator.OfInt to test, but when the intended operation is forEachRemaining, you would need both, an IntConsumer to be passed to the PrimitiveIterator.OfInt for efficient processing and a Consumer<Integer> to handle those iterators which are not instances of PrimitiveIterator.OfInt and if you implement both within one class, you don’t need to perform a test at all, the iterator will do for you:

public static void main(String[] args) {
    System.out.println("with Collection (of Integer boxes)");
    filterAndPrint(Arrays.asList(1, 2, 3), i -> i>2);
    System.out.println("with IntStream (using primitive int values)");
    filterAndPrint(() -> IntStream.range(1, 4).iterator(), i -> i>2);
}
interface LoggingUnboxingIntConsumer extends IntConsumer, Consumer<Integer> {
    @Override default void accept(Integer t) {
        System.out.println("  unboxing " + t);
            accept(t.intValue());
    }
}
public static void filterAndPrint(Iterable<Integer> i, IntPredicate p) {
    i.iterator().forEachRemaining((LoggingUnboxingIntConsumer) (int value) -> {
        if(p.test(value)) System.out.println("  value "+value+" matches");
    });
}
with Collection (of Integer boxes)
  unboxing 1
  unboxing 2
  unboxing 3
  value 3 matches
with IntStream (using primitive int values)
  value 3 matches

This demonstrates that boxing operations are avoided when possible. This is part of the contract of PrimitiveIterator.OfInt.forEachRemaining(Consumer<? super Integer>):

Implementation Requirements:

If the action is an instance of IntConsumer then it is cast to IntConsumer and passed to forEachRemaining(java.util.function.IntConsumer); otherwise the action is adapted to an instance of IntConsumer, by boxing the argument of IntConsumer, and then passed to forEachRemaining(java.util.function.IntConsumer).

This does not apply to single element processing via hasNext()/next(), but since your code is supposed to perform composition of PrimitiveIterable.OfInt only, the initial step is the only place where adaptation has to be done anyway

public static PrimitiveIterator.OfInt adapt(Iterator<Integer> it) {
    return it instanceof PrimitiveIterator.OfInt? (PrimitiveIterator.OfInt)it:
      new PrimitiveIterator.OfInt() {
        public int nextInt() { return it.next(); }
        public boolean hasNext() { return it.hasNext(); }
        public Integer next() { return it.next(); }
      };
}

After having created this method once, you may use it at all places where you accept an Iterable, e.g.

public static FilterIteratorInt filter(Iterable<Integer> iter, IntPredicate filter) {
    return new FilterIteratorInt(adapt(iter.iterator()), filter);
}

But note that this “iterator composition” looks very much like a reinvention of the Stream API (or IntStream API specifically)…

like image 186
Holger Avatar answered Oct 11 '22 14:10

Holger