I have often thought it would be a good idea to allow the use of arrays as proper objects with their own methods instead of relying on helper classes like Arrays, Arrays and ArrayUtils.
For example:
ints.sort(); // Arrays.sort(ints);
int[] onemore = ints.add(8); // int[] onemore = ArrayUtils.add(ints, 8);
I am sure I am not the first with this idea but I have had trouble searching for others who have written about this before. Can anyone help me with some references on this topic?
Is this thought to be a good or bad idea, and why?
How easy would this be to implement?
Some other examples might include (but don't get hung up on them, they're extraneous to the question itself):
int[] ints = {5,4,3,2,1};
// Arrays.sort (ints);
ints.sort();
// int pos = Arrays.asList(ints).indexOf (5);
// int pos = ArraysUtils.indexOf (ints, 5);
int pos = ints.indexOf (5);
// Arrays.reverse (ints);
ints.reverse();
Array<Integer> array = ints; // cast to super class.
// int length = Array.getLength (array);
int length = array.getLength();
// Object n = Array.get (array, 3);
Object n = array.get (3);
// Array.set (array, 3, 7);
array.set (3, 7);
Object obj = array;
// if (obj instanceof int[])
// System.out.println(Array.toString((int[]) obj));
// else if (....)
System.out.println (obj);
Arrays are not classes in Java for a good reason - they map well onto people's understanding of how an array should work from experience with C-style languages. There are also performance reasons for making arrays low-level containers rather than objects. Because of this, sometimes there are performance benefits to using a primitive array rather than a Collection.
If you want to use objects, you should just use a Collection (an ArrayList is an example of a collection). It can be clunky, but Collections provide the type of nice methodological access that you seem to want.
Those methods start to look an awful lot like ruby or python idioms. Unfortunately you don't get to do that in java (wish you could).
For one, as others have pointed out, the collections classes do it for you. For another, myarray.sort() isn't so nice because you can create arrays of objects for which sorting has not been defined. Suppose I have
Foo[] foos;
And Foo is not Comparable. What happens on foos.sort()? We definitely wouldn't want to have it only work for primitives
int[] ints;
ints.sort(); //legal
Object[] objects;
objects.sort(); //illegal
and you certainly couldn't have the compiler only allow the syntax for comparable objects. And once you get to something like
myarray.add(new Foo());
it's sort of pointless, as arrays in java aren't growable.
It would be nice if printing out an array didn't give you that useless
([I'm an array(*&(*
rubbish, though.
Yes. But I'm of the opinion that Java should not have any primitives at all. I think primitives in Java are a break from the cleanness of the language. Everything in Java should be objects. Whether or not the objects are allocated on the stack or the heap should be an implementation detail of the JVM not a language construct. But I think my opinion might be more radical than most.
Before autoboxing, dealing with primitives and objects was very cumbersome.
If arrays were objects and could be autoboxed (and generisized!), we could have something like
Array<Integer> myArray = new Integer[];
myArray.add(8);
int x = myArray[0];
....
or
Array<Class<? extends MyBaseObject>> myArray = {ExtendedObject.class}
ExtendedObject object = myArray[0].newInstance();
....
Yes, I believe that arrays should have an API defined beyond the one specified in the language itself. In particular, it's annoying that a Foo[] doesn't implement Iterable<Foo>. Yes, I know it's easy to wrap it - but it's annoying that you have to.
.NET gets this mostly right, with the Array type which is mostly for non-generic access, and various generic interfaces which are deemed to be implemented by the actual arrays. For instance:
IList<int> intList = new int[10];
(It helps that .NET generics cope with primitive types better than Java does.)
The only downsides I've seen of this approach is that arrays can be covariant in .NET, but normal generics aren't, and that things get slightly confusing with non-zero-based arrays and rectangular arrays.
As an aside, various people have referred to arrays as primitives in this thread. While they certainly have special handling, they're not defined to be primitives. From the language spec, section 4.2:
A primitive type is predefined by the Java programming language and named by its
reserved keyword (§3.9):
PrimitiveType:
NumericType
boolean
NumericType:
IntegralType
FloatingPointType
IntegralType: one of
byte short int long char
FloatingPointType: one of
float double
Before I answer for SHOULD, I will tell you the state of this issue.
In Java, arrays are considered objects -- you can store them in a List for example. However they are special objects in that they inherit from Object but are not instantiated or accessed with the same syntax (thanks for the correction Peter). They are aggressively optimized by the JVM for obvious performance issues, and thus the way an array is stored is dependent on implementation.
So the Sun argument would be that, if they gave an object API for the array, it could require certain hardware, software, or other specification features to be in place.
The only real interface for arrays are the System.arrayCopy static method or the Array.* static methods, which will most efficiently copy to/from arrays.
In response to SHOULD; it has been solved, although a standard would be better than the answer: use ArrayList.
It is not a BAD idea. Thing is it has already been done on Collections. Try extending ArrayList if you wanna go crazy.
I believe this is very hard to implement without breaking compatibility at many levels.
JVM treats arrays little differently than other objects. Creation/initialization is different: no constructor is called for one thing. How would you want to add new methods? If by adding superclass, would you call its constructor?
Arrays know type of element in runtime, generics don't. If you wanted to add new superclass for all arrays (like you suggested in original question), would you make it generic? Also keep in mind that arrays are covariant in Java, generics aren't.
Arrays have fixed set of methods/fields specified in Java Language Specification (all methods from Objects, which are explicitely named in JLS, length field). Adding new members is likely to break existing clients depending on this. (i.e. arrays are not your random class)
Array serialization would probably be affected too
I am sure there are more implementation details which would make this extremly hard :-(
Programs compiled to work with new methods would not work on older JVMs. What's worse, depending on implementation, programs compiled for old JVMs may not work with modified arrays on new JVMs.
I would like to have methods directly in "Array class", I don't think it's possible to implement it now.
I personally like the idea of everything being an object, and this has already been done in smalltalk, for instance. However, the consequences of making everything, even methods/funcitons an object are very far-reaching (have a look at smalltalk to see what I mean). Being consistent in the application of the "everything is an object"-rule results in a language that doesn't look anything like C (or Java) anymore.
Java was very consciously designed to be approachable to C programmers, and it has succeeded at that, but it's not actually very object oriented compared to smalltalk. It's heritage as a C variant shows up all over the place, like in arrays and the fact that there are primitives.
So to answer the question of SHOULD, I would say no, because Java wouldn't be Java anymore with that change. As others have pointed out, there are other classes that let you handle tasks which might have been done with an array in C using objects in Java, but to get rid of primitive data types and arrays alltogether is a task better left to a different language, IMHO.
For those who missed a previous post which was closed. Some other examples include
int[] ints = {5,4,3,2,1};
ints.sort(); // instead of Arrays.sort(ints);
int pos = ints.indexOf(5); // instead of Arrays.asList(ints).indexOf(5); or ArraysUtils.indexOf(ints, 5);
ints.reverse(); // instead of Arrays.reverse(ints);
Array<Integer> array = ints; // cast to super class.
int length = array.getLength(); // instead of Array.getLength(array);
Object n = array.get(3); // instead of Array.get(array, 3);
array.set(3, 7); // instead of Array.set(array, 3, 7);
Object obj = array;
System.out.println(obj); // prints [5,4,7,2,1] instead of having to
// if (obj instanceof int[]) System.out.println(Array.toString((int[]) obj)); else if (....)
int[] ints2 = ints.copyOf(2);
int[] ints3 = ints.subArray(2,4);
ints.sort(myComparator);
List<Integer> list = ints.asList();
Set<Integer> set = ints.asSet();
long total = ints.sum();
double avg = int.average();
int max = ints.max();
int max2 = ints.max(myComparator);
http://commons.apache.org/lang/api/org/apache/commons/lang/ArrayUtils.html
int[] onemore = ints.add(8); // instead of ArrayUtils.add(ints, 8);
int[] moreInts = ints.addAll(ints2); // instead of ArraysUtils.addAll(ints, ints2);
int[] oneless = int.remove(3); // instead of ArrayUtils.remove(ints, 3);
Integer[] integers = int.toObject();
int[] intsAgain = integers.toPrimitive();
One reason I believe it would be not so hard is that you can add methods indirectly by modifying Object (A real hack I agree, but it shows it works)
By adding methods to Object, the following code
int[] ints = {5, 4, 3, 2, 1};
System.out.println("ints= "+ints);
ints.sort();
System.out.println("after sort() ints= "+ints);
ints = ints.add(6);
System.out.println("after add(6) ints= "+ints);
Prints
ints= [5, 4, 3, 2, 1]
after sort() ints= [1, 2, 3, 4, 5]
after add(6) ints= [1, 2, 3, 4, 5, 6]
This works with Java 5 & 6 and both the compiler and IDE handled this as I expected.
Related
How do I get an array slice of an ArrayList in Java? Specifically I want to do something like this:
ArrayList<Integer> inputA = input.subList(0, input.size()/2);
// where 'input' is a prepouplated ArrayList<Integer>
So I expected this to work, but Java returns a List - so it's incompatible. And when I try to cast it, Java won't let me. I need an ArrayList - what can I do?
In Java, it is good practice to use interface types rather than concrete classes in APIs.
Your problem is that you1 are using ArrayList (probably in lots of places) where you should really be using List. As a result you created problems for yourself with an unnecessary constraint that the list is an ArrayList.
This is what your code should look like:
List input = new ArrayList(...);
public void doSomething(List input) {
List inputA = input.subList(0, input.size()/2);
...
}
this.doSomething(input);
1 - Based on your comments, "you" was actually someone else ... who set this problem in an interview question. It is possible that this was actually a trick question, designed to see how you would cope with creating a (real) slice of an ArrayList that was a assignment compatible with ArrayList.
Your proposed "solution" to the problem was/is this:
new ArrayList(input.subList(0, input.size()/2))
That works by making a copy of the sublist. It is not a slice in the normal sense. Furthermore, if the sublist is big, then making the copy will be expensive.
If you are constrained by APIs that you cannot change, such that you have to declare inputA as an ArrayList, you might be able to implement a custom subclass of ArrayList in which the subList method returns a subclass of ArrayList. However:
It would be a lot of work to design, implement and test.
You have now added significant new class to your code base, possibly with dependencies on undocumented aspects (and therefore "subject to change") aspects of the ArrayList class.
You would need to change relevant places in your codebase where you are creating ArrayList instances to create instances of your subclass instead.
The "copy the array" solution is more practical ... bearing in mind that these are not true slices.
I have found a way if you know startIndex and endIndex of the elements one need to remove from ArrayList
Let al be the original ArrayList and startIndex,endIndex be start and end index to be removed from the array respectively:
al.subList(startIndex, endIndex + 1).clear();
If there is no existing method then I guess you can iterate from 0 to input.size()/2, taking each consecutive element and appending it to a new ArrayList.
EDIT: Actually, I think you can take that List and use it to instantiate a new ArrayList using one of the ArrayList constructors.
Although this post is very old. In case if somebody is looking for this..
Guava facilitates partitioning the List into sublists of a specified size
List<Integer> intList = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8);
List<List<Integer>> subSets = Lists.partition(intList, 3);
This is how I solved it. I forgot that sublist was a direct reference to the elements in the original list, so it makes sense why it wouldn't work.
ArrayList<Integer> inputA = new ArrayList<Integer>(input.subList(0, input.size()/2));
Array DS requires all its members to have the same time. Java throws ArrayStoreException when an attempt has been made to store the wrong type of object into an array of objects. Don't remember what C++ does.
Is my understanding correct that it's important to have all objects of the same type in array because it guarantees constant time element access through the following two operations:
1) element size * element index = offset
2) array pointer address + offset
If objects are of different types and consequently different size, the above mentioned formula won't work.
Because: we want it like this.
What I mean is: people using the Java language (probably the same for C++) are using a statically typed language for a purpose.
And when such people starting thinking in plurals; they typically think in plurals of "similar" things.
Caveat: in Java, everything is an Object, so you can always declare an Object[] and stuff anything into that. Strings, Numbers, whatever.
And that also leads to the other important aspect: in C++, your array represents an area in memory. And you better have same sized elements in that area; to avoid data corruption.
In Java on the other hand, an array is not pointing to raw memory.
Long story short: there are real differences between Java and C++ in this context (that one has to understand to make an informed decision); and then there is the "language" thing itself. In other words: this is not Ruby land, where you just put ducks, numbers, plants and quack sounds in the same "list" without further thinking.
Final thought, based on that joke in the last paragraph: in my eyes, an array is an implementation of the list concepts, thus it is about a collection of things of the same nature. If you want a collection of unrelated things, I would rather call that a tuple.
Yes, you are right. All that is required for constant time random access.
Also, you can have an array of void pointers if you want to store different data types in a single array. For instance in c++, do
void * a[N]
a[i] = (void *)(&YourClass)
Similarly, use Object[] in java.
The C++ language (and compiler) requires the type of the elements to be stored in an array for various reasons, like pointer arithmetics and array subscription (e.g. x[i]), default initialisation of the elements, dealing with alignment restrictions, ...).
int x[3] = { 1,2,3 }; // array of 3 int values, each being properly aligned concerning processor architecture;
myObjectType objs[10]; // array of 10 objects of type myObjectType, each being default initialised (probably the default constructor), each being properly aligned
myObjectType *objs[10]; // array of 10 pointers to objects of type myObjectType (including subclasses of myObjectType; allowing dynamic binding and polymorphism). Note: all pointers have the same size, the object to which they point may differ insize.
int *intptr = x;
bool isEqual= (intptr[2] == x[2]); // gives true
intptr += 2; // increases the pointer by "2*sizeof(int)" bytes.
So, yes, you are right: one reason is because of calculating offsets; But there are other reasons as well, and other issues like alignment, array to pointer decay, default initialisation logic are probably more subtle but essential, too.
I have an array of ints that I want to use to instantiate a array or list of objects. In my case, the old-fashoned way to do it would be:
int[] layer_sizes = {784, 500, 10};
Layer[] layers = new Layer[layer_sizes.length];
for (int i=0; i<layer_sizes.length; i++)
layers[i] = new Layer(layer_sizes[i]);
But now I see Java 8 has all these fancy streams. I now want to do something like Python's list comprehensions:
List<Layer> layers = Stream.of(layer_sizes).map(size -> Layer(size));
But it doesn't let me do that, and I'm not sure why... The message it gives is
incompatible types: no instance(s) of type variable(s) R exist so that Stream<R> comforms to List<Layer> where R, T are type variables....
Is there a way to use Streams to construct an array of objects in one line?
EDIT: Not a duplicate of previous question, because it turns out that there're some peculiarities of making streams from primitives.
Conclusion
Thank you Sam Sun and Eran. The line I ended up using was this:
Layer[] layers = Arrays.stream(layer_sizes).boxed().map(Layer::new).toArray(Layer[]::new);
Whatever boxed() is, you need it, unless you declare layer_sizes as an Integer instead of int.
P.S. If the java developers are reading this, it would be amazing for Java 9 or whatever's next to have something like
Layer[] layers = {new Layer(size) for (size:layer_sizes)} // OR at least:
Layer[] layers = Stream.of(layer_sizes).map(Layer::new).toArray()
You are missing two things - collecting the Stream into a List and invoking the Layer constructor (you are missing the new keyword) :
List<Layer> layers =
IntStream.of(layer_sizes)
.mapToObj(size -> new Layer(size))
.collect(Collectors.toList());
And if you wish your output to be an array instead of a List, call toArray instead of collect.
EDIT :
I just realized that Stream.of, when passed an int[], would produce a Stream<int[]>, not a Stream<Integer>. Therefore, you should use IntStream, which handles primitive int elements.
The alternative is to replace the input int[] layer_sizes = {784, 500, 10}; with Integer[] layer_sizes = {784, 500, 10};.
Eran's answer has the general idea of what to do, but is missing a few key details.
Using Stream.of on an int[] will result in a Stream<int[]>; one of the magical artifacts of Java.
Instead, you should use Arrays.stream or IntStream.of to get a IntStream (remember, primitives can't be a parameter).
For the map operation, you can use a method reference to Layer::new.
This all boils down to this new snippit
List<Layer> layers = IntStream.of(layer_sizes).boxed().map(Layer::new).collect(Collectors.toList());
Generally, Java can be considered as a type-safe language. I know that there are some flaws with generics, but I recently came across a Problem I never had before.
To break it down:
Object[] objects = new Integer[10];
objects[0] = "Hello World";
will NOT result in a compile-time error as expected. I would assume that the declaration of an Array of Object will disallow to point to to an array of something else. In Generics I'm not allowed to make such weird things like:
ArrayList<Object> objs = new ArrayList<Integer>
and if I try to kind of trick Java into doing something with
ArrayList<? extends Object> objects = new ArrayList<Integer>
I'm allowed to declare it, but I can only add Objects of type null.
Why doesn't Java prevent the declaration of such weired arrays?
Firstly, I should point out that this is type-safe.
Object[] objects = new Integer[10];
objects[0] = "Hello World";
because an exception will be thrown. (It is not statically type-safe ... but that is a different statement entirely.)
The reason that Java allows this is historical. Until Java 5, Java did not support any form of generics. Gosling has said that if they had had the time to figure out and incorporate generics into Java 1.0, they would have done so.
Unfortunately, they didn't. But they still wanted to be able write things like a general purpose sort method with the following signature:
void sort(Object[] array, Comparator comp) ...
To make this method work for any kind of object array (without generics), it was necessary to make arrays covariant; i.e. to make it legal to pass a String[] or Integer[] as an argument where the formal type is Object[]. If they hadn't done that you would have had to copy the String[] to an Object[], sort it, and then copy it back.
I don't think there's an answer to this besides "legacy design". (Which I admit is a fancy way of saying "because".) You pretty much need to be able to do an equivalent of the last assignment you show somehow. (Otherwise you're stuck to making lots and lots of copies with manual up/down casts, assuming language features of Java pre 1.4)
In Java 1 when type semantics for arrays were basically set in stone, generics weren't available, or even up for consideration for a long while yet. So there was no mechanism available to express the higher-order type constraints needed to make this construct type-safe – and Gosling (IIRC a fan of simplicity) felt resolving this edge case of compile-time type safety wasn't worth complicated the language with whichever solutions were available. Or wasn't bothered by doing the check at runtime enough to even look for a solution. (At the end of the day language design decisions are arbitrary to at least some degree, and there's only one person that could answer this with any certainty.)
"Because it has to".
To elaborate a bit, consider the following example:
Object[] objects = null;
if (something) {
objects = new Integer[10];
} else {
objects = new String[10];
}
Now, how would the Java compiler know which assignments to allow and which to refuse? It can't. The compile-time type is Object so the compiler will let you put any Object in your array, simply because it doesn't have any knowledge of the runtime type of your array.
actually in case of arrays you get a exception at run time called ArrayStoreException when you add wrong type of element In this case a String. In case of generics there is no such exception. its for the very same reason you are not allowed to add anything but object, as you might just add a wrong type into the list.
There's Discussion that I found while I google it
I Found:
Firstly, arrays do not break type safety. If they did, then upcasting
an array wouldn't fail at runtime. They make it impossible for the
compiler to prove the program type-safe, so the checking is deferred
until runtime.
I think confusion occurs here because one the one hand, since a String
is-a Object an array of Strings is-obviously-an array of Objects, and
on the other it clearly isn't. The answer is in the mutability of an
array.
If the array is immutable, then it safe to treat a String[] as an
Object[], because an immutable String[] is always exactly like an
immutable Object[].
On the other hand, if the array is mutable, then it is not usually
safe to treat a String[] as an Object[].
The "wildcards" technique described in the above link is exactly what
CommonLisp has been doing for years.
(deftype StringArray? () (array String)) ; This is the type of arrays of String
(deftype ObjectArray? () (array Object)) ; This is the type of arrays of Object
(subtypep StringArray? ObjectArray?) ; Is StringArray? a subtype of ObjectArray?? false, true ; No, it isn't. (false: it isn't, true: I'm ure)
(deftype AnyArray? () (array *)) ; This is the type of arrays of anything (subtypep StringArray? AnyArray?) ; Is StringArray? a subtype of AnyArray?? true, true ; Yes, it is. (true: it is, true: I'm sure)
I need to create a dynamic array in Java, but the values type differ from String to Int to float. how can I create a dynamic list that I don't need to give it in advanced the type of the values?
The keys just need to be ascending numbers (1,2,3,4 or 0,1,2,3,4)
I checked ArrayList but it seems that I have to give it a fixed type for the values.
thanks!
You can have an array or an ArrayList of Objects which will allow you to contain String, int, and float elements.
You can use this:
List<Object> myList = new ArrayList<Object>();
Integer i = 1;
Double d = 1.2;
String s = "Hello World";
myList.add(i);
myList.add(d);
myList.add(s);
It's pretty rare, in my experience, to want a List<Object>. I think it might be a design smell, and I'd examine the design to see if another set of structures might better represent your data. Without knowing anything about what you're trying to solve, it's hard to say with any confidence, but typically one wants to do things with what one has put into a list, and to do anything meaningful with things once they're just Object, you'll need to examine their type and get reflective, to kind of break away from language basics. Versus storing them in more type-sensitive structures, where you can deal directly with them in their original types without reflection magic.
It's more trouble than it's worth, but it is possible to interact with arrays reflectively.
import java.lang.reflect.Array;
// ...
Object arr = Array.newInstance(int.class, 10);
System.out.println(arr.getClass().getName()); // prints "[I"
System.out.println(Array.getLength(arr)); // prints "10"
Array.set(arr, 5, 42);
if (arr instanceof int[]) {
int[] nums = (int[]) arr;
System.out.println(nums[5]); // prints "42"
}
References
java.lang.reflect.Array API
Do note that in the API you pass arrays as Object. This is because Object is the superclass of all array types, be it int[].class or String[][].class. This also means that there is little compile time safety (as is true with reflection in general). Array.getLength("mamamia") compiles just fine; it'll throw an IllegalArgumentException at runtime.