33

I was going through Java documentation, and I learned that methods in the Arrays class in Java are all static. I don't really understand the reason behind why they made it static.

For example, the following code violates the OO approach, because if I have a type, 'X', then all the methods which acts on it should be inside it:

int[] a = {34, 23, 12};
Arrays.sort(a);

It would be better if they have implemented the following way:

int[] a = {34, 23, 12};
a.sort();

Can anyone explain me a bit on this?

Community
  • 1
  • 1
Aadhil
  • 470
  • 7
  • 15
  • See also http://stackoverflow.com/questions/8546500/why-isnt-there-a-java-lang-array-class-if-a-java-array-is-an-object-shouldnt – Raedwald Aug 25 '16 at 07:14

8 Answers8

41

In Java there is no way to extend the functionally of an array. Arrays all inherit from Object but this gives very little. IMHO This is a deficiency of Java.

Instead, to add functionality for arrays, static utility methods are added to classes like Array and Arrays. These methods are static as they are not instance methods.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • 6
    But java arrays have a `length` property which doesn't come from `Object`. So why could other properties or even methods not be added? – Asaph Aug 23 '16 at 05:03
  • 'length' is built inside the language and not defined in any class – Aadhil Aug 23 '16 at 05:05
  • 9
    @Asaph the `length` read only property is defined in the JLS and has it's own byte code instruction. `arraylength` Extending this is not as simple as adding methods to a class. – Peter Lawrey Aug 23 '16 at 05:05
  • 6
    I agree that this is a deficiency in Java's design. At the very least, the instance versions of equals(), hashCode(), and toString() should've been implemented properly for array classes. But they refuse to do this because of stringent backwards compatibility policy. –  Aug 23 '16 at 05:07
  • 4
    @friendlydog you can add to `toString()` by changing the implementation of `Object.tostring()` http://vanillajava.blogspot.com.au/2016/03/printing-arrays-by-hacking-jvm.html – Peter Lawrey Aug 23 '16 at 05:08
  • Even when this deficiency isn't there, the same pattern is often used to bolt optional functionality on top of existing classes, e.g. [`Objects`](https://docs.oracle.com/javase/8/docs/api/java/util/Objects.html) or [`Collections`](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html) classes. – biziclop Aug 23 '16 at 13:45
  • @Peter Lawrey: so, when this trick works for `toString()`, it should work for `equals` and `hashCode()` similarly, shouldn’t it? Well, right, that could really cause compatibility problems. – Holger Aug 23 '16 at 16:25
  • 2
    While I agree with the 2nd half of this answer, I find the first to be incorrect. Oracle (through the JCP) could have updated the language specification to add a sort method to array. However, in Java arrays are much more primitive than arrays in many of the newer languages, so I do think adding a sort method wouldn't be a good idea, but if they were motivated enough they could do it. Using utility classes makes more sense in this case. – Robert Aug 23 '16 at 17:39
  • @Robert they should/could have defined a class file for each array type and added the methods there. However there was a desire to not change the byte code or jvm model which lead to this sort of work around. – Peter Lawrey Aug 23 '16 at 22:17
  • @Holger the problem with hashCode is that it is a native method which means changing it in C code. – Peter Lawrey Aug 23 '16 at 22:22
  • @Robert: changes of this kind are possible and there was a time when array types were changed to implement `Serializable` and `Cloneable`, but this requires changes in the JVM, which the language designers try to keep at a minimum. On the other hand, it’s easy to specify the existence of a `sort()` method for array types at the language level which compilers simply convert to `Arrays.sort` on the byte code level. But to me it seems that the language designer postpone any related change in favor of the bigger picture they have in mind (value types, `long` index, extendable array types, etc.). – Holger Aug 24 '16 at 08:42
  • @Peter Lawrey: nobody says that the replacement of a `native` method has to be a `native` method as well. The original operation still is available via `System.identityHashCode()`. However, I don’t know whether it works for the particular method `Object.hashCode()`, which the JVM might treat as an intrinsic operation. – Holger Aug 24 '16 at 08:47
  • @PeterLawrey Yes, it wouldn't have been a good solution, and as noted would likely have involved breaking changes. Therefore it's easy to see why it really wouldn't be done, and wouldn't be practical nor likely desirable to do. – Robert Aug 25 '16 at 02:28
20

Good observation. Observe also that not every array can be sorted. Only arrays of primitives and Objects which implement the Comparable interface can be sorted. So a general sort() method that applies to all arrays is not possible. And so we have several overloaded static methods for each of the supported types that are actually sortable.

Update:

@Holger correctly points out in the comments below that one of the overloaded static methods is indeed Arrays.sort(Object[]) but the docs explicitly state:

All elements in the array must implement the Comparable interface.

So it doesn't work for Objects that don't implement Comparable or one of its subinterfaces.

Community
  • 1
  • 1
Asaph
  • 159,146
  • 25
  • 197
  • 199
  • 1
    Actually, there are methods for primitive types and there is `Arrays.sort(Object[])`. Which array type do you have in mind, when saying that there is no general `sort()` method? Note that the mentioned variant even works flawlessly, when using a non-comparable array type but comparable elements, e.g. `Object[] array={ "foo", "bar" }; Arrays.sort(array);`… – Holger Aug 23 '16 at 16:30
  • The [docs for `Arrays.sort(Object[])`](https://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#sort-java.lang.Object:A-) explicitly say "All elements in the array must implement the Comparable interface.". This is probably a carry over from the pre-generics era. Also, [`java.lang.String`](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html) *does* implement the [`Comparable`](https://docs.oracle.com/javase/8/docs/api/index.html?java/lang/Comparable.html) interface. – Asaph Aug 23 '16 at 16:35
  • 1
    Yes, as I said, the *elements* are comparable, but the array type is `Object[]` which is not a subtype of `Comparable[]`. But, as said, this constraint is not enforced by the signature. Your answer creates the impression that the provided methods kinda sort out arrays that are not comparable, whereas in fact you can pass in any array and will only notice at runtime, when the elements aren’t comparable (and this hasn’t changed with generics). – Holger Aug 23 '16 at 16:46
  • My guess is it probably hasn't changed with the introduction of generics for the sake of backwards compatibility. But it would certainly be preferable to have a compile time error instead of a runtime exception. In any case, the point that an array sort() method that works in the general case (i.e. all Objects) is not possible still stands. – Asaph Aug 23 '16 at 16:51
  • I will grant you that the original wording of my answer glosses over some subtleties. I've since updated it. – Asaph Aug 23 '16 at 17:41
  • 1
    `Arrays.sort(Comparable[])` would not work because generics and arrays don't usually play nicely together: methods taking arrays must generally accept [reifiable types](https://docs.oracle.com/javase/tutorial/java/generics/nonReifiableVarargsType.html), which generics are not due to type erasure. –  Aug 23 '16 at 19:43
  • @Snowman: `Arrays.sort(Object[])` was introduced in Java 1.2, long before Generics became a thing. Besides that, there is no problem in declaring a generic array parameter type. It’s the actual array type you pass in, which must have a reifiable element type. There is quite a different practical problem that collections like `ArrayList` have an `Object[]` array as backend so restricting the array type parameter of `Arrays.sort` would imply that you can’t pass through that array, even if you know that all elements are comparable. – Holger Aug 24 '16 at 08:57
  • 1
    @Asaph: of course, signatures had to stay the same for backwards compatibility when Generics were introduced. E.g. note that `Comparable` now has the generic signature `Comparable` rather than `Comparable>` (like with `Enum>`). The reason is that, for whatever reason, the original pre-Generics signature of the `compare` method is `compare(Object)` rather than `compare(Comparable)`. Hence, the generic signature had to be defined in a way that the erased signature of `compare(T)` stays `compare(Object)`. Tricky. – Holger Aug 24 '16 at 09:06
13

First of all, Arrays is an utility class, which does exactly that: exposes static methods. It is separate from any arr[] instances and has no OO relation to it. There are several classes like that, like Collections or various StringUtils.

Arrays are collections, they are used to store data. Arrays.sort() is an algorithm which sorts the collection. There may be many other algorithms which sort data in different way, all of them would be used in the same way: MyAlgorithm.doSthWithArray(array). Even if there was a sort() method on an array (it would then have to be a SortableArray, because not all Objects can be sorted automatically), all other algorithms would have to be called the old way anyway. Unless there was a visitor pattern introduced... But that makes things too complicated, hence, there is no point.

For a java Collection there's Collections.sort(), even in C++ there is std::sort which works similarly, as does qsort in C . I don't see a problem here, I see consistency.

Dariusz
  • 21,561
  • 9
  • 74
  • 114
  • 2
    Note that in Java 8 there is a `List.sort` instance method. – Boris the Spider Aug 23 '16 at 06:59
  • Which uses `Arrays.sort()` internally as interface's default implementation... It's just syntactic sugar. And I dislike it being added to the interface. – Dariusz Aug 23 '16 at 07:06
  • @It's not even remotely "syntactic sugar" - it's a library method. Syntactic sugar is, for example, `(a, b) -> a + b`. I see your point re adding it to the `interface`, but disagree with it; methods may throw exceptions for a variety of reasons including misuse. Calling `List.sort` on a `List` where `T` isn't `Comparable super T>` is a misuse and results in an exception. – Boris the Spider Aug 23 '16 at 09:43
  • 1
    Agreed, it's not syntactic sugar, let's call it convenience method. Which is quite inconvenient because it may introduce a runtime exception where normally a compile-time check would locate the error. – Dariusz Aug 23 '16 at 09:53
  • `std::sort` is C++, not C which does not have namespaces ;-) – Lucas Trzesniewski Aug 23 '16 at 11:55
  • 2
    This is the proper answer that diagnoses the OP's (understandable) misunderstanding: `Arrays` is not the type of an array, hence its static methods could not be member functions of arrays without altering the language. This is different from wrapper types like `Integer` etc. (These wrappers are, btw., hybrids in that they *are* value-carrying objects as well as utility classes for the underlying primitives by providing static methods like `static String toString(int i)`.) – Peter - Reinstate Monica Aug 23 '16 at 13:43
  • 2
    @Dariusz: it’s not just a convenience method as, unlike the `static` method in `Collections`, implementations can now override the `sort` method, which the widely used `ArrayList` does for efficiency, whereas lists returned by `synchronizedList` do that to provide thread safety which wasn’t there in previous releases. Regarding type safety, trying to sort lists naturally without a declared comparable element type always worked via `Collections.sort(list, null)`. If you don’t like it, you can use `list.sort(Comparator.naturalOrder())` which *does* the check exactly like `Collections.sort(list)` – Holger Aug 23 '16 at 16:41
  • @Holger comparator-less `Collections.sort()` has templated T extends `Comparable`. – Dariusz Aug 23 '16 at 20:06
  • 1
    @Dariusz: yes, but as said, you can bypass that check by using `Collections.sort(list, null)`. So you have the choice of using the checked comparator-less `Collections.sort(list)` or the unchecked `Collections.sort(list, null)`. Likewise, you have a choice when calling sort on a list, use the checked `list.sort(Comparator.naturalOrder())` or the unchecked `list.sort(null)`. Unfortunately, it is impossible to offer a checked comparator-less `list.sort()`. – Holger Aug 24 '16 at 09:28
3

Static Methods are sometimes used for utility purpose. So Arrays is utility class for general purpose array operations. Similarly, Collections is also Util class where utility methods are given.

Shatayu Darbhe
  • 797
  • 1
  • 7
  • 13
2

Arrays are kind of like second-class generics. When you make an array it makes a custom class for the array type, but it's not full featured because they decided how arrays would work before they really fleshed out the language.

That, combined with maintaining backwards compatibility, means that Arrays are stuck with an archaic interface.

It's just an old part of the API.

QuestionC
  • 10,006
  • 4
  • 26
  • 44
2

An array is not an object which stores state, beyond the actual values of int the array. In other words, it's just a "dumb container". It doesn't "know" any behaviour.

A utility class is a class which has just public static methods which are stateless functions. Sorting is stateless because there's nothing remembered between calls to that method. It runs "standalone", applying its formula to whatever object is passed in, as long as that object is "sortable". A second instance of an Arrays class would have behaviour no different, so just have the one static instance.

As Dariusz pointed out, there are different ways of sorting. So you could have MyArrays.betterSort(array) as well as Arrays.sort(array).

If you wanted to have the array "know" how best to sort its own members, you'd have to have your own array class which extends an array.

But what if you had a situation where you wanted different sorting on different times on the the same array? A contrived example, maybe, but there are plenty of similar real-world examples.

And now you're getting complicated. Maybe an array of type T sort differently than type S ....

It's made simple with a static utility and the Comparator<T> interface.

Stewart
  • 17,616
  • 8
  • 52
  • 80
1

For me this is the perfect solution. I have an array, and I have a class, Arrays, which operates over the data in the array. For example, you may want to hold some random numbers and you will never want to sort or any other utility method you will receive behavior which you don't want. That's why in code design it is good to separate data from the behavior.

You can read about the single responsibility principle.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Petar Petrov
  • 586
  • 2
  • 10
  • 28
  • Just wondering (never worked with Java): You might also have a string `"abc"` that you only need to print, yet it comes with a bunch of instance methods. Aren't you essentially getting behavior which you don't want? Or is there some difference that I'm missing (because I don't know the language)? – ROAL Aug 24 '16 at 07:01
1

The Arrays class contains methods that are independent of state, so therefore they should be static. It's essentially a utility class.

While OOP principles don't apply, the current way is clearer, concise, and more readable since you don't have to worry about polymorphism and inheritance. This all reduces scope, which ultimately reduces the chances that you screw something up.

Now, you may ask yourself "Why can't I extend the functionality of an array in Java?". A nice answer is that this introduces potential security holes, which could break system code.

Community
  • 1
  • 1
heez
  • 2,029
  • 3
  • 27
  • 39