Related
The TL;DR version, for those who don't want the background, is the following specific question:
Question
Why doesn't Java have an implementation of true multidimensional arrays? Is there a solid technical reason? What am I missing here?
Background
Java has multidimensional arrays at the syntax level, in that one can declare
int[][] arr = new int[10][10];
but it seems that this is really not what one might have expected. Rather than having the JVM allocate a contiguous block of RAM big enough to store 100 ints, it comes out as an array of arrays of ints: so each layer is a contiguous block of RAM, but the thing as a whole is not. Accessing arr[i][j] is thus rather slow: the JVM has to
find the int[] stored at arr[i];
index this to find the int stored at arr[i][j].
This involves querying an object to go from one layer to the next, which is rather expensive.
Why Java does this
At one level, it's not hard to see why this can't be optimised to a simple scale-and-add lookup even if it were all allocated in one fixed block. The problem is that arr[3] is a reference all of its own, and it can be changed. So although arrays are of fixed size, we could easily write
arr[3] = new int[11];
and now the scale-and-add is screwed because this layer has grown. You'd need to know at runtime whether everything is still the same size as it used to be. In addition, of course, this will then get allocated somewhere else in RAM (it'll have to be, since it's bigger than what it's replacing), so it's not even in the right place for scale-and-add.
What's problematic about it
It seems to me that this is not ideal, and that for two reasons.
For one, it's slow. A test I ran with these methods for summing the contents of a single dimensional or multidimensional array took nearly twice as long (714 seconds vs 371 seconds) for the multidimensional case (an int[1000000] and an int[100][100][100] respectively, filled with random int values, run 1000000 times with warm cache).
public static long sumSingle(int[] arr) {
long total = 0;
for (int i=0; i<arr.length; i++)
total+=arr[i];
return total;
}
public static long sumMulti(int[][][] arr) {
long total = 0;
for (int i=0; i<arr.length; i++)
for (int j=0; j<arr[0].length; j++)
for (int k=0; k<arr[0][0].length; k++)
total+=arr[i][j][k];
return total;
}
Secondly, because it's slow, it thereby encourages obscure coding. If you encounter something performance-critical that would be naturally done with a multidimensional array, you have an incentive to write it as a flat array, even if that makes the unnatural and hard to read. You're left with an unpalatable choice: obscure code or slow code.
What could be done about it
It seems to me that the basic problem could easily enough be fixed. The only reason, as we saw earlier, that it can't be optimised is that the structure might change. But Java already has a mechanism for making references unchangeable: declare them as final.
Now, just declaring it with
final int[][] arr = new int[10][10];
isn't good enough because it's only arr that is final here: arr[3] still isn't, and could be changed, so the structure might still change. But if we had a way of declaring things so that it was final throughout, except at the bottom layer where the int values are stored, then we'd have an entire immutable structure, and it could all be allocated as one block, and indexed with scale-and-add.
How it would look syntactically, I'm not sure (I'm not a language designer). Maybe
final int[final][] arr = new int[10][10];
although admittedly that looks a bit weird. This would mean: final at the top layer; final at the next layer; not final at the bottom layer (else the int values themselves would be immutable).
Finality throughout would enable the JIT compiler to optimise this to give performance to that of a single dimensional array, which would then take away the temptation to code that way just to get round the slowness of multidimensional arrays.
(I hear a rumour that C# does something like this, although I also hear another rumour that the CLR implementation is so bad that it's not worth having... perhaps they're just rumours...)
Question
So why doesn't Java have an implementation of true multidimensional arrays? Is there a solid technical reason? What am I missing here?
Update
A bizarre side note: the difference in timings drops away to only a few percent if you use an int for the running total rather than a long. Why would there be such a small difference with an int, and such a big difference with a long?
Benchmarking code
Code I used for benchmarking, in case anyone wants to try to reproduce these results:
public class Multidimensional {
public static long sumSingle(final int[] arr) {
long total = 0;
for (int i=0; i<arr.length; i++)
total+=arr[i];
return total;
}
public static long sumMulti(final int[][][] arr) {
long total = 0;
for (int i=0; i<arr.length; i++)
for (int j=0; j<arr[0].length; j++)
for (int k=0; k<arr[0][0].length; k++)
total+=arr[i][j][k];
return total;
}
public static void main(String[] args) {
final int iterations = 1000000;
Random r = new Random();
int[] arr = new int[1000000];
for (int i=0; i<arr.length; i++)
arr[i]=r.nextInt();
long total = 0;
System.out.println(sumSingle(arr));
long time = System.nanoTime();
for (int i=0; i<iterations; i++)
total = sumSingle(arr);
time = System.nanoTime()-time;
System.out.printf("Took %d ms for single dimension\n", time/1000000, total);
int[][][] arrMulti = new int[100][100][100];
for (int i=0; i<arrMulti.length; i++)
for (int j=0; j<arrMulti[i].length; j++)
for (int k=0; k<arrMulti[i][j].length; k++)
arrMulti[i][j][k]=r.nextInt();
System.out.println(sumMulti(arrMulti));
time = System.nanoTime();
for (int i=0; i<iterations; i++)
total = sumMulti(arrMulti);
time = System.nanoTime()-time;
System.out.printf("Took %d ms for multi dimension\n", time/1000000, total);
}
}
but it seems that this is really not what one might have expected.
Why?
Consider that the form T[] means "array of type T", then just as we would expect int[] to mean "array of type int", we would expect int[][] to mean "array of type array of type int", because there's no less reason for having int[] as the T than int.
As such, considering that one can have arrays of any type, it follows just from the way [ and ] are used in declaring and initialising arrays (and for that matter, {, } and ,), that without some sort of special rule banning arrays of arrays, we get this sort of use "for free".
Now consider also that there are things we can do with jagged arrays that we can't do otherwise:
We can have "jagged" arrays where different inner arrays are of different sizes.
We can have null arrays within the outer array where appropriate mapping of the data, or perhaps to allow lazy building.
We can deliberately alias within the array so e.g. lookup[1] is the same array as lookup[5]. (This can allow for massive savings with some data-sets, e.g. many Unicode properties can be mapped for the full set of 1,112,064 code points in a small amount of memory because leaf arrays of properties can be repeated for ranges with matching patterns).
Some heap implementations can handle the many smaller objects better than one large object in memory.
There are certainly cases where these sort of multi-dimensional arrays are useful.
Now, the default state of any feature is unspecified and unimplemented. Someone needs to decide to specify and implement a feature, or else it wouldn't exist.
Since, as shown above, the array-of-array sort of multidimensional array will exist unless someone decided to introduce a special banning array-of-array feature. Since arrays of arrays are useful for the reasons above, that would be a strange decision to make.
Conversely, the sort of multidimensional array where an array has a defined rank that can be greater than 1 and so be used with a set of indices rather than a single index, does not follow naturally from what is already defined. Someone would need to:
Decide on the specification for the declaration, initialisation and use would work.
Document it.
Write the actual code to do this.
Test the code to do this.
Handle the bugs, edge-cases, reports of bugs that aren't actually bugs, backward-compatibility issues caused by fixing the bugs.
Also users would have to learn this new feature.
So, it has to be worth it. Some things that would make it worth it would be:
If there was no way of doing the same thing.
If the way of doing the same thing was strange or not well-known.
People would expect it from similar contexts.
Users can't provide similar functionality themselves.
In this case though:
But there is.
Using strides within arrays was already known to C and C++ programmers and Java built on its syntax so that the same techniques are directly applicable
Java's syntax was based on C++, and C++ similarly only has direct support for multidimensional arrays as arrays-of-arrays. (Except when statically allocated, but that's not something that would have an analogy in Java where arrays are objects).
One can easily write a class that wraps an array and details of stride-sizes and allows access via a set of indices.
Really, the question is not "why doesn't Java have true multidimensional arrays"? But "Why should it?"
Of course, the points you made in favour of multidimensional arrays are valid, and some languages do have them for that reason, but the burden is nonetheless to argue a feature in, not argue it out.
(I hear a rumour that C# does something like this, although I also hear another rumour that the CLR implementation is so bad that it's not worth having... perhaps they're just rumours...)
Like many rumours, there's an element of truth here, but it is not the full truth.
.NET arrays can indeed have multiple ranks. This is not the only way in which it is more flexible than Java. Each rank can also have a lower-bound other than zero. As such, you could for example have an array that goes from -3 to 42 or a two dimensional array where one rank goes from -2 to 5 and another from 57 to 100, or whatever.
C# does not give complete access to all of this from its built-in syntax (you need to call Array.CreateInstance() for lower bounds other than zero), but it does for allow you to use the syntax int[,] for a two-dimensional array of int, int[,,] for a three-dimensional array, and so on.
Now, the extra work involved in dealing with lower bounds other than zero adds a performance burden, and yet these cases are relatively uncommon. For that reason single-rank arrays with a lower-bound of 0 are treated as a special case with a more performant implementation. Indeed, they are internally a different sort of structure.
In .NET multi-dimensional arrays with lower bounds of zero are treated as multi-dimensional arrays whose lower bounds just happen to be zero (that is, as an example of the slower case) rather than the faster case being able to handle ranks greater than 1.
Of course, .NET could have had a fast-path case for zero-based multi-dimensional arrays, but then all the reasons for Java not having them apply and the fact that there's already one special case, and special cases suck, and then there would be two special cases and they would suck more. (As it is, one can have some issues with trying to assign a value of one type to a variable of the other type).
Not a single thing above shows clearly that Java couldn't possibly have had the sort of multi-dimensional array you talk of; it would have been a sensible enough decision, but so also the decision that was made was also sensible.
This should be a question to James Gosling, I suppose. The initial design of Java was about OOP and simplicity, not about speed.
If you have a better idea of how multidimensional arrays should work, there are several ways of bringing it to life:
Submit a JDK Enhancement Proposal.
Develop a new JSR through Java Community Process.
Propose a new Project.
UPD. Of course, you are not the first to question the problems of Java arrays design.
For instance, projects Sumatra and Panama would also benefit from true multidimensional arrays.
"Arrays 2.0" is John Rose's talk on this subject at JVM Language Summit 2012.
To me it looks like you sort of answered the question yourself:
... an incentive to write it as a flat array, even if that makes the unnatural and hard to read.
So write it as a flat array which is easy to read. With a trivial helper like
double get(int row, int col) {
return data[rowLength * row + col];
}
and similar setter and possibly a +=-equivalent, you can pretend you're working with a 2D array. It's really no big deal. You can't use the array notation and everything gets verbose and ugly. But that seems to be the Java way. It's exactly the same as with BigInteger or BigDecimal. You can't use braces for accessing a Map, that's a very similar case.
Now the question is how important all those features are? Would more people be happy if they could write x += BigDecimal.valueOf("123456.654321") + 10;, or spouse["Paul"] = "Mary";, or use 2D arrays without the boilerplate, or what? All of this would be nice and you could go further, e.g., array slices. But there's no real problem. You have to choose between verbosity and inefficiency as in many other cases. IMHO, the effort spent on this feature can be better spent elsewhere. Your 2D arrays are a new best as....
Java actually has no 2D primitive arrays, ...
it's mostly a syntactic sugar, the underlying thing is array of objects.
double[][] a = new double[1][1];
Object[] b = a;
As arrays are reified, the current implementation needs hardly any support. Your implementation would open a can of worms:
There are currently 8 primitive types, which means 9 array types, would a 2D array be the tenth? What about 3D?
There is a single special object header type for arrays. A 2D array could need another one.
What about java.lang.reflect.Array? Clone it for 2D arrays?
Many other features would have be adapted, e.g. serialization.
And what would
??? x = {new int[1], new int[2]};
be? An old-style 2D int[][]? What about interoperability?
I guess, it's all doable, but there are simpler and more important things missing from Java. Some people need 2D arrays all the time, but many can hardly remember when they used any array at all.
I am unable to reproduce the performance benefits you claim. Specifically, the test program:
public abstract class Benchmark {
final String name;
public Benchmark(String name) {
this.name = name;
}
abstract int run(int iterations) throws Throwable;
private BigDecimal time() {
try {
int nextI = 1;
int i;
long duration;
do {
i = nextI;
long start = System.nanoTime();
run(i);
duration = System.nanoTime() - start;
nextI = (i << 1) | 1;
} while (duration < 1000000000 && nextI > 0);
return new BigDecimal((duration) * 1000 / i).movePointLeft(3);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
#Override
public String toString() {
return name + "\t" + time() + " ns";
}
public static void main(String[] args) throws Exception {
final int[] flat = new int[100*100*100];
final int[][][] multi = new int[100][100][100];
Random chaos = new Random();
for (int i = 0; i < flat.length; i++) {
flat[i] = chaos.nextInt();
}
for (int i=0; i<multi.length; i++)
for (int j=0; j<multi[0].length; j++)
for (int k=0; k<multi[0][0].length; k++)
multi[i][j][k] = chaos.nextInt();
Benchmark[] marks = {
new Benchmark("flat") {
#Override
int run(int iterations) throws Throwable {
long total = 0;
for (int j = 0; j < iterations; j++)
for (int i = 0; i < flat.length; i++)
total += flat[i];
return (int) total;
}
},
new Benchmark("multi") {
#Override
int run(int iterations) throws Throwable {
long total = 0;
for (int iter = 0; iter < iterations; iter++)
for (int i=0; i<multi.length; i++)
for (int j=0; j<multi[0].length; j++)
for (int k=0; k<multi[0][0].length; k++)
total+=multi[i][j][k];
return (int) total;
}
},
new Benchmark("multi (idiomatic)") {
#Override
int run(int iterations) throws Throwable {
long total = 0;
for (int iter = 0; iter < iterations; iter++)
for (int[][] a : multi)
for (int[] b : a)
for (int c : b)
total += c;
return (int) total;
}
}
};
for (Benchmark mark : marks) {
System.out.println(mark);
}
}
}
run on my workstation with
java version "1.8.0_05"
Java(TM) SE Runtime Environment (build 1.8.0_05-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.5-b02, mixed mode)
prints
flat 264360.217 ns
multi 270303.246 ns
multi (idiomatic) 266607.334 ns
That is, we observe a mere 3% difference between the one-dimensional and the multi-dimensional code you provided. This difference drops to 1% if we use idiomatic Java (specifically, an enhanced for loop) for traversal (probably because bounds checking is performed on the same array object the loop dereferences, enabling the just in time compiler to elide bounds checking more completely).
Performance therefore seems an inadequate justification for increasing the complexity of the language. Specifically, to support true multi dimensional arrays, the Java programming language would have to distinguish between arrays of arrays, and multidimensional arrays.
Likewise, programmers would have to distinguish between them, and be aware of their differences. API designers would have to ponder whether to use an array of arrays, or a multidimensional array. The compiler, class file format, class file verifier, interpreter, and just in time compiler would have to be extended. This would be particularly difficult, because multidimensional arrays of different dimension counts would have an incompatible memory layout (because the size of their dimensions must be stored to enable bounds checking), and can therefore not be subtypes of each other. As a consequence, the methods of class java.util.Arrays would likely have to be duplicated for each dimension count, as would all otherwise polymorphic algorithms working with arrays.
To conclude, extending Java to support multidimensional arrays would offer negligible performance gain for most programs, but require non-trivial extensions to its type system, compiler and runtime environment. Introducing them would therefore have been at odds with the design goals of the Java programming language, specifically that it be simple.
Since this question is to a great extent about performance, let me contribute a proper JMH-based benchmark. I have also changed some things to make your example both simpler and the performance edge more prominent.
In my case I compare a 1D array with a 2D-array, and use a very short inner dimension. This is the worst case for the cache.
I have tried with both long and int accumulator and saw no difference between them. I submit the version with int.
#OutputTimeUnit(TimeUnit.NANOSECONDS)
#BenchmarkMode(Mode.AverageTime)
#OperationsPerInvocation(X*Y)
#Warmup(iterations = 30, time = 100, timeUnit=MILLISECONDS)
#Measurement(iterations = 5, time = 1000, timeUnit=MILLISECONDS)
#State(Scope.Thread)
#Threads(1)
#Fork(1)
public class Measure
{
static final int X = 100_000, Y = 10;
private final int[] single = new int[X*Y];
private final int[][] multi = new int[X][Y];
#Setup public void setup() {
final ThreadLocalRandom rnd = ThreadLocalRandom.current();
for (int i=0; i<single.length; i++) single[i] = rnd.nextInt();
for (int i=0; i<multi.length; i++)
for (int j=0; j<multi[0].length; j++)
multi[i][j] = rnd.nextInt();
}
#Benchmark public long sumSingle() { return sumSingle(single); }
#Benchmark public long sumMulti() { return sumMulti(multi); }
public static long sumSingle(int[] arr) {
int total = 0;
for (int i=0; i<arr.length; i++)
total+=arr[i];
return total;
}
public static long sumMulti(int[][] arr) {
int total = 0;
for (int i=0; i<arr.length; i++)
for (int j=0; j<arr[0].length; j++)
total+=arr[i][j];
return total;
}
}
The difference in performance is larger than what you have measured:
Benchmark Mode Samples Score Score error Units
o.s.Measure.sumMulti avgt 5 1,356 0,121 ns/op
o.s.Measure.sumSingle avgt 5 0,421 0,018 ns/op
That's a factor above three. (Note that the timing is reported per array element.)
I also note that there is no warmup involved: the first 100 ms are as fast as the rest. Apparently this is such a simple task that the interpreter already does all it takes to make it optimal.
Update
Changing sumMulti's inner loop to
for (int j=0; j<arr[i].length; j++)
total+=arr[i][j];
(note arr[i].length) resulted in a significant speedup, as predicted by maaartinus. Using arr[0].length makes it impossible to eliminate the index range check. Now the results are as follows:
Benchmark Mode Samples Score Error Units
o.s.Measure.sumMulti avgt 5 0,992 ± 0,066 ns/op
o.s.Measure.sumSingle avgt 5 0,424 ± 0,046 ns/op
If you want a fast implementation of a true multi-dimentional array you could write a custom implementation like this. But you are right... it is not as crisp as the array notation. Although, a neat implementation could be quite friendly.
public class MyArray{
private int rows = 0;
private int cols = 0;
String[] backingArray = null;
public MyArray(int rows, int cols){
this.rows = rows;
this.cols = cols;
backingArray = new String[rows*cols];
}
public String get(int row, int col){
return backingArray[row*cols + col];
}
... setters and other stuff
}
Why is it not the default implementation?
The designers of Java probably had to decide how the default notation of the usual C array syntax would behave. They had a single array notation which could either implement arrays-of-arrays or true multi-dimentional arrays.
I think early Java designers were really concerned with Java being safe. Lot of decisions seem to have been taken to make it difficult for the average programmer(or a good programmer on a bad day) to not mess up something . With true multi-dimensional arrays, it is easier for users to waste large chunks of memory by allocating blocks where they are not useful.
Also, from Java's embedded systems roots, they probably found that it was more likely to find pieces of memory to allocate rather than large chunks of memory required for true multi-dimentional objects.
Of course, the flip side is that places where multi-dimensional arrays really make sense suffer. And you are forced to use a library and messy looking code to get your work done.
Why is it still not included in the language?
Even today, true multi-dimensional arrays are a risk from the the point of view of possible of memory wastage/misuse.
I want to copy an array in less than time proportional to N^2. I'm just using a double for loop right now...
int[][] copy = new int[N][N];
for(int i = 0; i < N; i++){
for(int j = 0; j < N; j++){
copy[i][j] = original[i][j];
}
}
Is there a faster way?
int[][] copy = new int[N][N];
for(int i = 0; i < N; i++){
System.arrayCopy(original[i], 0, copy[i], 0, N);
}
http://docs.oracle.com/javase/7/docs/api/java/lang/System.html#arraycopy(java.lang.Object,%20int,%20java.lang.Object,%20int,%20int)
Java is JIT compiled, and the native code compiler can do all sorts of clever things to make simple loops go fast. So it is not obvious that a implementation of arraycopy makes a significant difference.
So the answer provided by #Brett Okken doesn't have to be faster than yours. But to be "sure" you can use his way. It's the "fastest".
System.arrayCopy(original[i], 0, copy[i], 0, N);
I know I'm late to the party, but maybe this is useful to most people:
IF YOU'RE JUST STORING THE DATA ON A NEW ARRAY BECAUSE THE ARRAY YOU'RE USING IS FULL, which means that the steps are:
1 -alloc a new and bigger array;
2 -copy the values from the older array to the new one;
3 -"point" the variable to the new array.
I've seen this algorithm:
int[][] newArray = new int[oldArray.length + 1][];
for (int n = oldArray.length; n-- != 0; newArray[n] = oldArray[n]) {}
oldArray = newArray;
The idea here is not to copy the values but to 'point' to them, this is specially usefull when the bigger structure is an 3d array. It seems that this algorithm is the fastest available.
This is not useful if you don't want to change the initial array, were, as it is obvious, the option is to send a copy of the original one.
It must be mentioned that I saw this code, or something close in an API named "fastUtil". It is available here
Please feel free to correct me if I said any nonsense.
Basically what I want is to I skip elements on those index values which are there in the set otherwise I should just push the old array elements into the new array.
So if my set contains [2, 4, 9, 10] I should skip the values at index 2,4,9,10 in the old Array and put the values at othe other index locations in my new Array.
I am writing code like this
int[] newArr = new int[oldArray.length - set.size()];
for(int i = 0, j = 0; j < newArr.length && i < oldArray.length; j++,i++){
if(set.contains(i) )
i++;
else
newArray[j] = oldArray[i];
}
I am creating and filling my set like this
Set<Integer> commonSet = new HashSet<>();
for(int i = 0; i < array1; i++ ){
for(int j= 0; j < array2; j++) {
if(arrayOne[i] == arrayTwo[j]){
commonSet.add(i);// Here I am saving the indices.
}
}
}
Not Sure if this is the best way. Is there any other way which would be more efficient?
Or I must have to resort to Collection classes like ArrayLists.
Using Collection classes instead of arrays would make your code much simpler.
Doing array subtraction using common libraries like apache CollectionUtils looks like this:
Collection<Integer> diff = CollectionUtils.subtract(Arrays.asList(array1), Arrays.asList(array2));
Unless you're going to be working very large sets of data, it won't have a noticeable impact on speed.
Also, creating a set of different indexes the way you do above is going to scale very poorly for larger data sets. Just calculating the times for doing a difference using CollectionUtils.subtract() vs your set creation code shows the scaling problems (arrays filled with random Integers):
array1.length = 1000
array2.length = 1000
diff.size() = 530
elapsed ms for calculating diff = 39
set creation ms = 7
array1.length = 10000
array2.length = 10000
diff.size() = 5182
elapsed ms for calculating diff = 47
set creation ms = 519
array1.length = 50000
array2.length = 50000
diff.size() = 26140
elapsed ms for calculating diff = 101
set creation ms = 12857
array1.length = 1000000
array2.length = 1000000
diff.size() = 524142
elapsed ms for calculating diff = 1167
(didn't bother to wait for the program to finish)
As you can see, doing a double loop to compare every element scales quite poorly, and that's not even counting the subtraction you'll have to do afterwards.
EDIT updated to reflect changes in the question
If you're worried about performance, definitely do not use any list or collection classes. They are notorious for re-allocating arrays frequently as they need more capacity, which is a very slow operation.
Unfortunately, I don't know how you create/fill the set of indices. If it is possible for you to have your set in an array as well and generate it in such a way that its entries are sorted, you can optimize your code significantly.
If set is fairly long compared to oldArray, do this (this assumes no duplicate entries in set!):
int l = oldArray.length; // Cache length (some compilers might do this for you)
for (int i=0, j=0, k=0; i<l; i++) {
if (set[k]==i) {
k++;
} else {
newArr[j++] = oldArray[i];
}
}
If set is fairly short, do this (this can handle duplicate entries, but set still needs to be sorted):
int o1=0;
int o2=0;
for (int p:set) {
System.arraycopy(oldArray, o1, newArr, o2, p-o1);
o1+=p+1;
o2+=p;
}
System.arraycopy(oldArray, o1, newArray, o2, oldArray.length-o1);
The former avoids function calls and the latter banks on the optimized memory-copy implementation of System.arraycopy(...) (and set can be any sorted Iterable, although an array will be faster).
Which one is faster will depend on the exact sizes of your arrays and which system (CPU, JVM) you use.
If set is not sorted, you can either use your approach (debugged, of course) or you can sort it first and then use one of the approaches here. Again, which one will give you better performance will depend on the size of set and your system.
This piece of code is doing it for me.
Thanks # Patricia Shanahan
int j = 0, i = 0;
while( j < newArr.length && i < oldArray.length){
if(commonSet.contains(i)){
i++;
}
else{
diffArray[j] = arrayOne[i];
j++;
i++;
}
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
In-place transposition of a matrix
Recently attended an Technical Written Interview. Came through the following question.
I have an array as say
testArray = {a1,a2,a3,...an,b1,b2,b3,....bn,c1,c2,c3,.....,cn}
I need to sort this array as `
testArray = {a1,b1,c1,a2,b2,c2,a3,b3,c3,.....,an,bn,cn}
Constraint is I should not use extra memory, should not use any inbuilt function.
Should write complete code, it can be in any language and can also use any data structure.
eg:
Input: {1,2,3,4,5,6,7,8,9}, n = 3
Output: {1,4,7,2,5,8,3,6,9}
I could not get any solution within the constraint, can anyone provide solution or suggestion?
This is just a matrix transpose operation. And there is even a problem and solution for in-place matrix transposition on Wikipedia.
No extra space is impossible, since you need to at least go through the array. O(1) additional memory is possible, with heavy penalty on the time complexity.
The solution is built on follow-the-cycle algorithm in the Wikipedia page: for each cell, we will find the cell with the smallest index in the cycle. If the cell with the smallest index is greater than or equal (>=) to the index of the current cell, we will perform chain swapping. Otherwise, we ignore the cell, since it has been swapped correctly. The (loosely analyzed) upper bound on time complexity can go as high as O((MN)2) (we go through M * N cells, and the cycle can only be as long as the total number of cells).
Impossibility
It is impossible to implement this algorithm without extra use of memory and an arbitrary length because you need a an iterator to traverse the list and that takes up space.
Finding the right indices to swap
For fixed lengths of the array and fixed n you can use a matrix transpose algorithm.
and in order to swap the elements y
The algorithm you are looking for is a matrix transpose algorithm.
so you have to swap every element exactly once iterating through it.
http://en.wikipedia.org/wiki/Transpose
basically you have to swap the m -th element in the n - th component with the n - th element in the m -th component. This can be done by a double loop.
m = length(array)/n;
for (i = 0; i < m; i++)
for (j = 0; j < n; j++)
{
index_1 = i * m + j;
index_2 = j * m + i
swap(index_1, index_2);
}
Note: For fixed m and n this loop can be completely unrolled and therefore m, i, j can be replaced by a constant.
Swaping without Memory consumption
In order to swap every element without using extra space you can use the XOR swap algorithm as pointed out in the comments:
X := X XOR Y
Y := Y XOR X
X := X XOR Y
The simplest way to swap two numbers (a and b) without using a temporary variable is like this:
b = b + a;
a = b - a;
b = b - a;
If you write that in a function, then you're part of the way there. How you keep track of which variable to swap within the arrays without using a temporary variable eludes me right now.
Bear in mind voters: he doesn't actually need to sort the array, just swap the right values.
Edit: this will work with large values in Java (and in C/C++ unless you turn on some very aggressive compiler optimisations - the behaviour is undefined but defaults to sane). The values will just wrap around.
Second edit - some (rather untested) code to flip the array around, with I think 4 integers over the memory limit. It's while technically massively unthreadsafe, but it would be parallelisable just because you only access each array location once at most:
static int[] a = {1,2,3,4,
5,6,7,8,
9,10,11,12,
13,14,15,16};
static int n = 4;
public static void main(String[] args)
{
for(int i = 0; i < a.length/n; i++) // 1 integer
for(int j = 0; j < n; j++) // 1 integer
if(j > i)
swap(i*n+j, j*n+i);
}
static void swap(int aPos, int bPos) // 2 integers
{
if(a[aPos] != a[bPos])
{
a[bPos] = a[aPos] + a[bPos];
a[aPos] = a[bPos] - a[aPos];
a[bPos] = a[bPos] - a[aPos];
}
}
Apologies if this misunderstands the question; I read it carefully and couldn't work out what was needed other than this.
Take a look at Quicksort algorithm
For more information about available algorithms, go to Sorting algorithm page.
I'm doing the following on my code:
double[][] temp=new double[0][2];
The program will run with no runtime exceptions. When I get the length of the temp like this temp.length it returns 0 and when I tried accessing the length of the inner arrays like this temp[0].length it always throws an ArrayIndexOutOfBoundsException. (That was only a test.)
Now I am wondering, Java did create a array with 0 length and at the same time an inner array with a length of 2 in an array with 0 length?
Did this kind of declaration has implications on memory management?
Will it develop complications on the coding and running the code?
Did Java really permit this kind of declaration?
In what sense did they permit this kind of declaration or did they just overlook this kind of situation?
And if they permit this declaration does it also has some special uses?
I was just exploring the possibility of doing this kind of declaration and had been questioning myself if this is really permissible.
Your opinions are gladly appreciated.
It is equivalent to
double[][] temp = new double[0][]; // a zero length array of double[]
for(int d=0; d<0; d++)
temp[d] = new double[2]; // whose each element is a new double[2]
of course the loop isn't executed, so there's no waste from "inner array"
see 15.10.1 Run-time Evaluation of Array Creation Expressions (JLS 3 - CHAPTER 15 Expressions)
If an array creation expression
contains N DimExpr expressions, then
it effectively executes a set of
nested loops of depth N-1 to create
the implied arrays of arrays. For
example, the declaration:
float[][] matrix = new float[3][3];
is equivalent in behavior to:
float[][] matrix = new float[3][];
for (int d = 0; d < matrix.length; d++)
matrix[d] = new float[3];
,so
double[][] temp=new double[0][2];
will be equivalent to
double[][] matrix = new double[0][];
for (int d = 0; d < 0; d++)
matrix[d] = new double[2];//would newer hepened
The only valid scenario ,I can think of is where you want to send and empty 2 dimensional array.
double[][] temp = new double[0][0];
return temp;
The above is a valid requirement in many matrix calculations.
Did this kind of declaration has
implications on memory management?
Not sure. And might also depends on the JVM to JVM implementations.
Will it develop complications on the
coding and running the code?
It should not if you are accessing the array in a loop like this
for(int i = 0; i<temp.length;i++)
for(int j=0; j<temp[i].length;j++)
{
// your code
}
Otherwise if you are accessing directly by using index then you should first check the index bounds.
Did Java really permit this kind of
declaration?
Yes. As I have said in the first statement.
In what sense did they permit this
kind of declaration or did they just
overlook this kind of situation?
As said before: A valid scenario is where you want to send and empty 2 dimensional array
There might be other scenarios.
And if they permit this declaration
does it also has some special uses?
Other than the my last answer I am not sure of any other scenario. But would love to know if they exist.