2

Okay, here is what I want to do:

I want to implement a crossover method for arrays.

It is supposed to take 2 arrays of same size and return two new arrays that are a kind of mix of the two input arrays. as in [a,a,a,a] [b,b,b,b] ------> [a,a,b,b] [b,b,a,a].

Now I wonder what would be the suggested way to do that in Java, since I cannot return more than one value.

My ideas are: - returning a Collection(or array) containing both new arrays.

I dont really like that one because it think would result in a harder to understand code. - avoiding the need to return two results by calling the method for each case but only getting one of the results each time.

I dont like that one either, because there would be no natural order about which solution should be returned. This would need to be specified, though resulting in harder to understand code.

Plus, this will work only for this basic case, but I will want to shuffle the array before the crossover and reverse that afterwards. I cannot do the shuffling isolated from the crossover since I wont want to actually do the operation, instead I want to use the information about the permutation while doing the crossover, which will be a more efficient way I think.

My question is not about the algorithm itself, but about the way to put in in a method(concerning input and output) in Java

HansDampf
  • 161
  • 2
  • 11

10 Answers10

11

Following a suggestion from Bruce Eckel's book Thinking in Java, in my Java projects I frequently include some utility classes for wrapping groups of two or three objects. They are trivial and handy, specially for methods that must return several objects. For example:

public class Pair<TA,TB> {
    public final TA a;
    public final TB b;

    /**
     * factory method
     */
    public static <TA,TB> Pair<TA,TB> createPair(TA a,TB b ){
        return new Pair<TA,TB>(a,b);
    }

    /**
     * private constructor - use instead factory method 
     */
    private Pair(final TA a, final TB b) {
            this.a = a;
            this.b = b; 
    }

    public String toString() {  
        return "(" + a + ", " + b + ")";
    }

}
leonbloy
  • 73,180
  • 20
  • 142
  • 190
  • Thats a good suggestion. But I am not sure if I am going to pick this one in this case. It would probably be like overdesigning and wont help to get it more clear, because my problem is rather simple. – HansDampf May 04 '10 at 17:09
  • You decide - but I doubt there is something more clean and clear than this approach. Notice that the class uses generics, it is not tied to your particular use case. And in you method you just have to make a return(Pair.createPair(arr1,arr2); in the end. – leonbloy May 04 '10 at 17:12
  • I did not decide that yet, maybe the additional class wont make it more complicated. As I see it now the question about how to decide may be what is the most efficient way. Will be using the "Wrapper" be less efficent than using an array directly. – HansDampf May 04 '10 at 17:18
  • Using a wrapper class will be slightly less efficient than using just arrays, however in the end, it should not be that much of a difference, and you will most likely find that any performance problems with your program will be found in somewhere else. In the end, you should use a code profiler to determine where you need to make changes. And that's even IF you need to make changes. – Thomas May 04 '10 at 17:35
6

Read the last section of this article:

http://www.yoda.arachsys.com/java/passing.html

To quote:

This is the real reason why pass by reference is used in many cases - it allows a method to effectively have many return values. Java doesn't allow multiple "real" return values, and it doesn't allow pass by reference semantics which would be used in other single-return-value languages. However, here are some techniques to work around this:

  1. If any of your return values are status codes that indicate success or failure of the method, eliminate them immediately. Replace them with exception handling that throws an exception if the method does not complete successfully. The exception is a more standard way of handling error conditions, can be more expressive, and eliminates one of your return values.

  2. Find related groups of return values, and encapsulate them into objects that contain each piece of information as fields. The classes for these objects can be expanded to encapsulate their behavior later, to further improve the design of the code. Each set of related return values that you encapsulate into an object removes return values from the method by increasing the level of abstraction of the method's interface. For instance, instead of passing co-ordinates X and Y by reference to allow them to be returned, create a mutable Point class, pass an object reference by value, and update the object's values within the method.

As a bonus, this section was updated by Jon Skeet :)

Community
  • 1
  • 1
DVK
  • 126,886
  • 32
  • 213
  • 327
5

If it is reasonable for the caller to know the size of the returned arrays ahead of time, you could pass them into the method:

     public void foo(Object[] inOne, Object[] inTwo, Object[] outOne, Object[] outTwo) {
            //etc.
     }

That being said, 90+% of the time multiple return values out of a method are hiding a better design. My solution would be to make the transformation inside an object:

     public class ArrayMixer {
           private Object[] one;
           private Object[] two;
           public ArrayMixer(Object[] first, Object[] second) {
                //Mix the arrays in the constructor and assign to one and two.
           }
           public Object[] getOne() { return one; }
           public Object[] getTwo() { return two; }
     }

I suspect that in your real use case that class and array one and array two can get better names.

Yishai
  • 90,445
  • 31
  • 189
  • 263
  • This is a refreshingly good idea, probably because it fits better to the domain I want to use the method in, which I regrettably left out. I can imagine having a class crossoverOperator(), I just would have to try out performance issues I guess. – HansDampf May 04 '10 at 17:27
  • If you don't know the size of the returned arrays, you could still pass two empty ArrayLists as parameters - the method (foo) could then resize them appropriately. – paulcm May 04 '10 at 18:13
3

Since the specification of your method is that it takes two input arrays and produces output arrays, I agree with you that the method should return both arrays at the same time.

I think that the most natural choice of return value is an int[][] of length 2 (substitute int with whatever type you are using). I don't see any reason it should make the code harder to understand, especially if you specify what the contents of the return value will be.

Edit: in response to your comment, I understand that you have considered this and I am saying that despite your stylistic objections, I don't believe there is a strictly "better" alternative ("better" here being loosely defined in the question).

An alternative approach, largely equivalent to this one, would be to define an object that wraps the two arrays. This has the small distinction of being able to refer to them by names rather than array indices.

danben
  • 80,905
  • 18
  • 123
  • 145
  • Good idea. How wasteful is this approach in terms of RAM and CPU? – Hamish Grubijan May 04 '10 at 17:00
  • Yes, as I mentioned I am considering this way. I just dont really feel comfortable with it, that is why I am asking if someone has any other ideas. The reason I dont feel comfortable is probably it feels so much like a work around, and everytime using it you gotta think about which entry contains which value. – HansDampf May 04 '10 at 17:02
  • @HansDamf It works, but it likely hides the real semantics you are interested in. Since arrays are jagged it will not perform different than using a discrete type, such as the Pair mentioned above. (E.g. this method is really no different than returning a Collection.) –  May 04 '10 at 17:09
  • as compared to in-place modification. – Hamish Grubijan May 04 '10 at 17:14
3

The best way to do it would be to do

public void doStuff(int[] array1, int[] array2) {
    // Put code here
}

Since Java arrays in Java pass the reference, any modifications made to the arrays will be made to the array itself. This has several caveats

  1. If you are setting it to null you must use a different way (such as encapsulating it in an object)
  2. If you are initializing the array (in the method), you must use a different way

You would use this in the format:

// other method
int[] array1 = new int[20];  // the arrays can be whatever size
int[] array2 = new int[20];

doStuff(array1,array2);

// do whatever you need to with the arrays

Edit: This makes the assumption that it is okay to make changes to the input arrays.

If it isn't, then an object (such as in leonbloy's answer is definitely what is called for).

Thomas
  • 4,889
  • 4
  • 24
  • 19
  • 2
    You are unnecessarily making the assumption that it is ok to destroy the input arrays. – danben May 04 '10 at 17:10
  • ... but the assumption that it is ok to destroy the input arrays might be valid (it isn't stated in the question). This is good if the arrays are large and the transformation would still work (saves allocating the result). If it is not ok, then the arrays could be cloned prior to passing to the method (resulting in a tradeoff between simpler method signature and more work for the caller). – paulcm May 04 '10 at 18:10
0

You strictly cannot return more then one value (think object or primitive) in Java. Maybe you could return an instance of a specific "Result" object which has the two arrays as properties?

ChristopheD
  • 112,638
  • 29
  • 165
  • 179
0

You could pass the output arrays as parameters to the method. This may give you more control over memory allocation for the arrays too.

t0ne
  • 159
  • 7
0

The cleanest and easiest to understand way would be to create a container bean that contains two arrays, and return the container from the method. I'd probably also pass in the container into the method, to keep it symmetric.

The most memory efficient way, assuming both arrays are the same length, would be to pass a multidimensional array - Object[2][n] - where n is the length of the arrays.

Dean J
  • 39,360
  • 16
  • 67
  • 93
0

If you're really against the arbitrary ordering that comes from a 2d array or a collection, perhaps consider making an inner class that reflects the logic of what you're doing. You could simply define a class that holds two arrays and you could have your method return that, with names and function that reflect the logic of exactly what you're doing.

JF.
  • 163
  • 1
  • 1
  • 7
0

A simple solution to the above problem is to return as Map.The trick of this question is how you will define the keys to identify the objects, let say there are two input arrays [a,a,a,a] [b,b,b,b] and two outputs arrays [a,a,b,b] [b,b,a,a]

For that you can use String variable as a key just to identify objects because String variable is immutable, so they can be used as keys. And as example

   Map<String,String[]> method(String[] x,String[] y){

do your stuff..........

   Hashmap<String,String[]> map =new HashMap<String,String[]>();
map.put("Object2",[b,b,a,a]);

return map;
}
Shashank T
  • 669
  • 1
  • 6
  • 10