Why is an array not assignable to Iterable?
Asked Answered
U

5

197

with Java5 we can write:

Foo[] foos = ...
for (Foo foo : foos) 

or just using an Iterable in the for loop. This is very handy.

However you can't write a generic method for iterable like this:

public void bar(Iterable<Foo> foos) { .. }

and calling it with an array since it is not an Iterable:

Foo[] foos = { .. };
bar(foos);  // compile time error 

I'm wondering about the reasons behind this design decision.

Uprear answered 21/7, 2009 at 15:49 Comment(14)
I'm kind of surprised that Google Collections doesn't have an array iterable class yet.Priedieu
Arrays.asList is good enough I supposeUprear
I forgot about that. So why don't you use asList then?Priedieu
Or is this just a philosophical question?Priedieu
it is a philosophical questionUprear
See also: #362867Kiosk
a good reason to deal with arrays in Java 5+ is varargs methods.Uralic
@MichaelMyers Would asList() not cause a non-trivial performance drop? Both while converting to/from and for get/settingScherer
@AaronJLang: Not really; the list returned by asList() uses the parameter array as backing, so the array doesn't have to be copied.Priedieu
@MichaelMyers asList returns an immutable List, which is unhandy if you want to perform changes on the List.Didst
@Torsten: true, but if you're passing it to a method that accepts an Iterable, you're likely not making any changes anyway.Priedieu
@MichaelMyers I just came across a method (of some framework) that accepted an Iterable and turned it into a List, but simply returned the Iterable if it was a List already. What I was looking for, was a method that accepted an array turning it to a mutable List. So turning the array to an immutable List with asList wouldn't get me any further, but that's probably a design fault of the method I found.Didst
Actually, Arrays.asList is not good enough because it doesn't work on arrays of primitive types. The only built-in way to generically iterate (boxed) elements of primitive types is with reflection, using java.lang.reflect.Array, but its performance is weak. However, you can write your own iterators (or List implementations!) to wrap arrays of primitive types if you want.Malodorous
I was looking for this question online. Actually I think it of this way. If the array lends itself to an indexed way of accessing its elements what is the need of an extra iterator? Its waste of work. Simple way of iterating is : for ( int i = 0; i < a.length; i++) a[i]. that's it. and the best part is you can access any element in O(1)Redon
U
81

Arrays can implement interfaces (Cloneable and java.io.Serializable). So why not Iterable? I guess Iterable forces adding an iterator method, and arrays don't implement methods. char[] doesn't even override toString. Anyway, arrays of references should be considered less than ideal - use Lists. As dfa comments, Arrays.asList will do the conversion for you, explicitly.

(Having said that, you can call clone on arrays.)

Udella answered 21/7, 2009 at 23:18 Comment(7)
> "... and arrays don't implement methods." I think this is another philosophical question; arrays weren't ever primitive types, and the Java philosophy reads that "Everything is an object (except primitive types)". Why, then, do arrays not implement methods even though there are a gazillion operations that one would want to use an array for from the start. Oh, that's right, arrays were the only strongly typed collections before generics came along as a sorry hindsight.Diglot
If you've got data in an array, it's possible that you're doing low-level, performance critical work such as dealing with byte[]'s read from streams. The inability to iterate arrays probably stems from Java generics not supporting primitives as type arguments, as @Gareth says below.Franks
@FatuHoku Suggesting generics were a sorry hindsight is incorrect. The desirability of generics was always appreciated. Arrays aren't primitives (I didn't say they were), but they are low-level. The one thing you want to do with arrays is use them as an implementation detail for vector-like structures.Udella
Iterator<T> also requires remove(T), though it is allowed to throw an UnsupportedOperationException.Aeolus
Arrays do implement methods: they implement all the methods of java.lang.Object.Manumission
@Manumission The methods of Object are implemented by Object for arrays.Udella
Ah, you're right, they just inherit the default implementations of toString, equals and hashCode, which are fairly useless for arrays.Manumission
C
65

The array is an Object, but its items might not be. The array might hold a primitive type like int, which Iterable can't cope with. At least that's what I reckon.

Copperplate answered 2/2, 2010 at 14:37 Comment(4)
This means in order to support the Iterable interface, primitive arrays must be specialized to use the wrapper classes. None of this is really a big deal though, since type parameters are all fake anyway.Frit
This would not prevent Object arrays from implementing Iterable. It would also not prevent primitive arrays from implementing Iterable for the wrapped type.Malodorous
Autoboxing could handle thisTressatressia
I think this is the correct reason. It will not work satisfactory for arrays of primitive types, until Generics support primitive types (e.g. List<int> instead of List<Integer>, etc). A hack could be done with wrappers but at a performance loss - and more importantly - if this hack was done, it'ld prevent implementing it properly in Java in the future (for example int[].iterator() would forever be locked to return Iterator<Integer> rather than Iterator<int>). Perhaps, the upcoming value-types + generic-specialization for Java (project valhalla) will make arrays implement Iterable.Josselyn
G
20

Arrays ought to support Iterable, they just don't, for the same reason that .NET arrays don't support an interface that allows readonly random access by position (there is no such interface defined as standard). Basically, frameworks often have annoying little gaps in them, which it's not worth anyone's time to fix. It wouldn't matter if we could fix them ourselves in some optimal way, but often we can't.

UPDATE: To be even-handed, I mentioned .NET arrays not supporting an interface that supports random access by position (see also my comment). But in .NET 4.5 that exact interface has been defined and is supported by arrays and the List<T> class:

IReadOnlyList<int> a = new[] {1, 2, 3, 4};
IReadOnlyList<int> b = new List<int> { 1, 2, 3, 4 };

All is still not quite perfect because the mutable list interface IList<T> doesn't inherit IReadOnlyList<T>:

IList<int> c = new List<int> { 1, 2, 3, 4 };
IReadOnlyList<int> d = c; // error

Maybe there is a possible backward compatibility gotcha with such a change.

If there's any progress on similar things in newer versions of Java, I'd be interested to know in the comments! :)

Gest answered 6/8, 2009 at 23:11 Comment(6)
.NET arrays implement the IList interfaceDowie
@Aphid - I said readonly random access. IList<T> exposes operations for modifying. It would be great if IList<T> had inherited something like a IReadonlyList<T> interface, which would have just had Count and T this[int] and inherited IEnumerable<T> (which already supports readonly enumeration). Another great thing would be an interface for obtaining a reverse-order enumerator, which the Reverse extension method could query for (just as the Count extension method queries for ICollection to optimise itself.)Gest
Yes, it would much be better if things had been designed that way. The IList interface does define IsReadOnly and IsFixedSize properties, which are implemented appropriately by array. That has always struck me as a very bad way of doing it though, as it offers no compile time checking that the list you are given is in fact readonly, and I very rarely see code which checks these properties.Dowie
Arrays in .NET implement IList & ICollection since .NET 1.1, and IList<T> and ICollection<T> since .NET 2.0. This is yet another case where Java is far behind the competition.Whoever
@TomGillen: My biggest problem with IList is that it doesn't provide more queryable attributes. I'd say a proper set should include IsUpdateable, IsResizable, IsReadOnly, IsFixedSize, and ExistingElementsAreImmutable for starters. The question of whether code a reference can, without typecasting, modify a list is separate from the questions of whether code which holds a reference to a list which it isn't supposed to modify can safely share that reference directly with outside code, or whether it can safely assume some aspect of the list will never change.Wernerwernerite
@TomGillen: I really don't think there's room in the type system to have different IWhateverKindOfLIst<T> interfaces for every combination of attributes. Further, trying to incorporate such details into the type system would complicate the design of wrapper classes.Wernerwernerite
S
16

Unfortunately, arrays aren't 'class-enough'. They don't implement the Iterable interface.

While arrays are now objects that implement Clonable and Serializable, I believe an array isn't an object in the normal sense, and doesn't implement the interface.

The reason you can use them in for-each loops is because Sun added in some syntatic sugar for arrays (it's a special case).

Since arrays started out as 'almost objects' with Java 1, it would be far too drastic of a change to make them real objects in Java.

Seasonal answered 21/7, 2009 at 15:52 Comment(15)
well, I'm wondering exactly why they don't implement the Iterable interface!Uprear
Since an array isn't an object in the normal sense, it can't implement the interface.Seasonal
@Uprear I'm afraid only Sun could really answer thatUda
they dont implement the iterable interface because they are not standard classes and cannot implement an interfaceOgilvie
Still, there's sugar for the for-each loop, so why can't there be sugar for Iterable?Priedieu
You would think that they could make an array act more like an iterable.Seasonal
@mmyers: The sugar used in for-each is compile-time sugar. That's a lot easier to do than VM sugar. Having said which, .NET arrays are significantly better in this area...Midriff
Arrays can implement interfaces. They implement Cloneable and Serializable interfaces.Yseulta
really ?Seasonal
java arrays are objects in all senses. Please remove that bit of misinformation. They just don't implement Iterable.Hatter
matt b: Most of the people who works on the 1.5 languages features no longer work for Sun.Udella
An array is an Object. It support useful :P methods like wait(), wait(n), wait(n,m), notify(), notifyAll(), finalize(), a pointless implementation of toString() The only useful method is getClass().Beezer
They could do the Iterable sugar compile-time too. If you passed an array to something which wanted Iterable, it could put the Arrays.asList() around it for you...Indaba
They could easily wrap arrays in standard library into something with methods, preserving old Object[] syntax for back compat. And things would have become more convenient. Everytime I encounter myself to work with java I feel like my hands tied. I'm happy scala has this in stdlib, and also finally collections have join...Chrome
Arrays should be iterable, but in classic Java fashion ... "implement feature 70% and call it done" ... and leave the developers to have to work around silly things such as this.Preposterous
O
5

The compiler actually translates the for each on an array into a simple for loop with a counter variable.

Compiling the following

public void doArrayForEach() {
    int[] ints = new int[5];

    for(int i : ints) {
        System.out.println(i);
    }
}

and then decompiling the .class file yields

public void doArrayForEach() {
    int[] ints = new int[5];
    int[] var2 = ints;
    int var3 = ints.length;

    for(int var4 = 0; var4 < var3; ++var4) {
        int i = var2[var4];
        System.out.println(i);
    }
}
Oler answered 19/4, 2020 at 20:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.