3

I am a fairly newbie programmer with a question on arrays in Java. Consider a 2D array, [i][j]. The value of i is determined at run time. The value of j is known to be 7. At [i][6] and [i][7] I want to be able to store a deeper array or list of values. Is it possible to have something like an array within an array, where there is an x and y axis and a z axis at the point of [i][6] and i[7] or will I need a full 3D cube of memory to be able to store and navigate my data?

The Details: My goal is to run a query which takes certain information from two tables (target and attacker) My query is fine and I can get a resultset. What I really want to be able to do is to store the data from my resultset and present it in a table in a more useful format while also using it in a data visualization program. The fields I get are: server_id, target_ip, threat_level, client_id, attacker_ip and num_of_attacks. I could get 20 records that have the same server_id, target_ip, threat_level, client_id but different attacker_ip and num_of_attacks because that machine got attacked 20 times. A third dimension would allow me to do this but the 3rd axis/array would be empty for server_id, target_ip, threat_level, client_id

UPDATE after reviewing the answers and doing some more thinking I'm wondering if using an arraylist of objects would be best for me, and/or possible. Keeping data organized and easily accessible is a big concern for me. In psedu code it would be something like this:

Object[] servers
    String server_id
    String target
    String threat_level
    String client_id
    String arr[][]   // this array will hold attacker_ip in one axis and num_of_attacks in the other in order to keep the relation between the attacking ip and the number of attacks they make against one specific server
exit_1
  • 1,240
  • 4
  • 13
  • 32
  • isn't this `int[][][]` what you are calling a "full 3D cube of memory? and i think that should work to you – jac1013 Aug 10 '13 at 05:48
  • @JosephChilberry I used "cube of memory" to describe the way I'm picturing the way to store data in my mind. [i][0] through [i][5] can only have one value. but [i][6] and [i][7] may need to hold many values. I may be doing a poor job explaining my problem, does this help? – exit_1 Aug 10 '13 at 05:51

3 Answers3

4

In first place, if you have an array DataType[i][j] and j is known to be 7, the 2 greatest indexes you can use are 5 and 6, not 6 and 7. This is because Java array indexes are 0-based. When creating the array you indicate the number of elements, not the maximum index (which always is one less than number of elements).

In second place, there is nothing wrong with using multidimensional arrays when the problem domain already uses them. I can think of scientific applications, data analysis applications, but not many more. If, on the contrary, you are modelling a business problem whose domain does not use multidimensional arrays, you are probably better off using more abstract data structures instead of forcing arrays into the design just because they seem very efficient, experience in other languages where arrays are more important, or other reasons.

Without having much information, I'd say your "first dimension" could be better represented by a List type (say ArrayList). Why? Because you say its size is determined at runtime (and I assume this comes indirectly, not as a magic number that you obtain from somewhere). Lists are similar to arrays but have the particularity that they "know" how to grow. Your program can easily append new elements as it reads them from a source or otherwise discovers/creates them. It can also easily insert them at the beginning or in the middle, but this is rare.

So, your first dimension would be: ArrayList<something>, where something is the type of your second dimension.

Regarding this second dimension, you say that it has a size of 7, but that the first 5 items accept single values while the last 2 multiple ones. This is already telling me that the 7 items are not homogeneous, and thus an array is ill-indicated. This dimension would be much better represented by a class. To understand this class's structure, let's say that the 5 single-valued elements are homogenous (of type, say, BigDecimal). One of the most natural representations for this is array, as the size is known. The 2 remaining, multi-valued elements also seem to constitute an array. However, given that each of its 2 elements contains an unidentified number of data items, the element type of this array should not be BigDecimal as in the previous case, but ArrayList. The type of the elements of these ArrayLists is whatever the type of the multiple values is (say BigDecimal too).

The final result is:

class SecondD {
    BigDecimal[] singleValued= new BigDecimal[5] ;
    ArrayList<BigDecimal>[] multiValued= new ArrayList<BigDecimal>[2] ;
    {
        multiValued[0]= new ArrayList<BigDecimal>() ;
        multiValued[1]= new ArrayList<BigDecimal>() ;
    }
}
ArrayList<SecondD> data= new ArrayList<SecondD>() ;

In this code snippet I'm not only declaring the structures, but also creating them so they are ready to use. Pure declaration would be:

class SecondD {
    BigDecimal[] singleValued;
    ArrayList<BigDecimal>[] multiValued;
}
ArrayList<SecondD> data= new ArrayList<SecondD>() ;

Array size is not important in Java from a type (and thus structural) point of view. That's why you don't see any 5 or 2.

Access to the data structure would be like

data.get(130).singleValued[2]
data.get(130).multiValued[1].get(27)

A possible variant that could be much clearer in certain cases is

class SecondD {
    BigDecimal monday;
    BigDecimal tuesday;
    BigDecimal wednesday;
    BigDecimal thursday;
    BigDecimal friday;
    ArrayList<BigDecimal> saturday= new ArrayList<BigDecimal>() ;
    ArrayList<BigDecimal> sunday= new ArrayList<BigDecimal>() ;
}
ArrayList<SecondD> data= new ArrayList<SecondD>() ;

In this case we are "expanding" each array into individual items, each with a name. Typical access operations would be:

data.get(130).wednesday
data.get(130).sunday.get(27)

Which variant to choose? Well, that depends on how similar or different the operations with the different itemes are. If every time you will perform and operation with monday you will also perform it with tuesday, wednesday, thursday, and friday (not saturday and sunday because these are a completely different kind of thing, remember?), then an array could be better. For example, to sum the items when stores as an array it's only necessary:

element= data.get(130) ;
int sum= 0 ;
for(int e: element.singleValued ) sum+= e ;

While if expanded:

element= data.get(130) ;
int sum= 0 ;
sum+= element.monday ;
sum+= element.tuesday ;
sum+= element.wednesday ;
sum+= element.thursday ;
sum+= element.friday ;

In this case, with only 5 elements, the difference is not much. The first way makes things slightly shorter, while the second makes them clearer. Personally, I vote for clarity. Now, if instead of 5 items they would have been 1,000 or even as few as 20, the repetition in the second case would have too much and the first case preferred. I have another general rule for this too: if I can name every element separately, then it's probably better to do exactly so. If while trying to name the elements I find myself using numbers or sequential letters of the alphabet (either naturally, as in the days of the month, or because things just don't seem to have different names), then it's arrays. You could still find cases that are not clear even after applying these two criteria. In this case toss a coin, start developing the program, and think a bit how things would be the other way. You can change your mind any time.

If your application is indeed a scientific one, please forgive me for such a long (and useless) explanation. My answer could help others looking for something similar, though.

Mario Rossi
  • 7,651
  • 27
  • 37
  • Wow great answer @Mario Rossi. I was just sort of going through a similar though process but in a much more blurry way. You're answer really helped me to see it clear and bring much more understanding to the situation. I believe that I will take this class route. – exit_1 Aug 10 '13 at 08:06
  • @solleks I ellaborated this response way before you added the "Details" section. Now I'm convinced my answer applies to your case. Specifically the 2nd version (the "days of the week" one). Just replace them with column names and you'll get use cases as `data.get(130).server_id` and `data.get(130).attacker_ip.get(27)`, which are extremely natural IMO. Glad it was useful to you. – Mario Rossi Aug 10 '13 at 09:14
  • @solleks Just read your UPDATE. You probably need a second class `class Attacker { String ipAddress; int attacks; }'`. The last item in your class could then be `ArrayList attackers;` instead of a 2D array. Example use cases: `data.get(130).server_id `, `data.get(130).attackers.get(27).ipAddress` and `data.get(130).attackers.get(27).attacks`. – Mario Rossi Aug 10 '13 at 09:22
  • thanks for the advice. I'm trying to make it work in eclipse right now and I'm a little lost on how to organize where I declare everything. Would you mind creating a simple demonstration that I can copy and paste into eclipse so I can play around with it. Sorry, I'm really new to Java programming. – exit_1 Aug 10 '13 at 09:50
  • @solleks I really don't know how big your application will be. But you can start with a single `class MyApplication` and put all: `static class Attacker`, `static class SecondD`, and probably method `static void main(String... args)` immediately under it. Why use `static class` instead of `class` in this situation? That would be another question. We can go only that far using comments only. – Mario Rossi Aug 12 '13 at 03:40
3

Use ArrayList instead of array primitives. You can have your three dimensions, without the associated inefficient wastage of allocating a "cube"


If not creating a custom class like @nIcE cOw suggested Collections are more cumbersome for this kind of thing than primitive arrays. This is because Java likes to be verbose and doesn't do certain things for you like operator overloading (like C++ does) or give you the ability to easily instantiate ArrayList from arrays.

To exemplify, heres @sbat's example with ArrayLists;

public static <T> ArrayList<T> toAL(T ... input) {
    ArrayList<T> output = new ArrayList<T>();
    for (T item : input) {
        output.add(item);
    }
    return output;
}

public static void main(String[] args) {
    ArrayList<ArrayList<ArrayList<Integer>>> a = toAL(
        toAL(
            toAL(0, 1, 2)
        ),
        toAL(
            toAL(4, 5)
        ),
        toAL(
            toAL(6)
        )
    );
    System.out.println(a.get(0).get(0).get(2));
    System.out.println(a.get(1).get(0).get(1));
    System.out.println(a.get(2).get(0).get(0));
}
Hashbrown
  • 12,091
  • 8
  • 72
  • 95
  • 1
    thanks @Hashbrown. I'm not familiar with ArrayLists but I'll look into it right now. – exit_1 Aug 10 '13 at 05:56
  • They're basically variable length arrays (not unlike [Vector](http://docs.oracle.com/javase/7/docs/api/java/util/Vector.html)s). – Hashbrown Aug 10 '13 at 05:59
  • 1
    +1, for better approach :-) @solleks, you can simply create a class like `Co_ordinates` and put the object references of this class to `ArrayList`. The best part is it can increase or shrink in size at runtime, unlike arrays which are static :-) – nIcE cOw Aug 10 '13 at 06:15
  • 1
    thanks @nIcEcOw. would I run into a problem doing this if what i was storing was coming from a resultset? sorry if this is trivial. – exit_1 Aug 10 '13 at 06:37
  • Your toAL method is the same as `new ArrayList(Arrays.asList(1, 2, 3));` – sgbj Aug 10 '13 at 06:47
  • you'd need to write a method to [convert the arrays](http://stackoverflow.com/questions/157944/how-to-create-arraylist-arraylistt-from-array-t) inside the `ResultSet` to `ArrayList`s. Sorry, my Java is pretty rusty – Hashbrown Aug 10 '13 at 06:47
  • 2
    @sbat, exchanging all the `toAL(`s with `new ArrayList(Arrays.asList(`s would be very wordy indeed. Not to mention that `X` isn't `Integer` like in your comment. It is `ArrayList>` in the first call, for example. Furthermore, creating a `List` *then* converting it to `ArrayList` is less efficient than creating, and filling, an `ArrayList` straight up – Hashbrown Aug 10 '13 at 06:56
  • Oh I know, and that's perfectly fine, I was just pointing it out. =) I generally code to the interface using List instead of ArrayList, so it'd be easy to do `List>> = asList(asList(asList(1, 2, 3)), asList(asList(1, 2), ...));` Especially if you `import static java.util.Arrays.*;` – sgbj Aug 10 '13 at 07:00
  • That's true, I had just committed to `ArrayList` in my answer, so I had to roll with it. – Hashbrown Aug 10 '13 at 07:01
  • Why create a new function when `Collections.addAll()` does exactly the same? Even if you want to save a few characters, you should have used it inside `toAL`. No `for` loop required at all. Also, the parametric type of the `ArrayList` will be inferred as the common supertype of all `input` elements. This could not be "super" enough for elements added afterwards. – Mario Rossi Aug 10 '13 at 08:52
  • @MarioRossi: replacing all `toAL(` with `Collections.addAll(new ArrayList(), ` would be immensely horrible. And using `addAll()` inside `toAL()` may be difficult to pass the variable argument array into. As for "not super enough", I think that's out of scope for the question, not only that but he states he's "fairly newbie" and trying to account for cases like that would yield an even more complicated answer – Hashbrown Aug 10 '13 at 09:10
  • @Hashbrown: True. I can't wait `Collection` literals are finally introduced into the language. And true: a more generic solution would probably require using the `object.method()` syntax. Definitely off topic. – Mario Rossi Aug 10 '13 at 09:54
2

Of course, there's nothing syntactically wrong with doing:

int[][][] a = {{{0, 1, 2}}, {{4, 5}}, {{6}}};
System.out.println(a[0][0].length); // 3
System.out.println(a[1][0].length); // 2
System.out.println(a[2][0].length); // 1

In fact, that's what multidimensional arrays in Java are, they're arrays within arrays. The only problem I see with this is that it might become confusing or difficult to maintain later on, but so would using ArrayLists within ArrayLists:

List<List<List<Integer>>> list = ...;
System.out.println(list.get(0).get(1).get(50)); // using ArrayList

However, there are still reasons as to why you might prefer an array over a collection. But ArrayLists or other collections may be preferable depending on the circumstance.

sgbj
  • 2,264
  • 17
  • 14
  • 2
    Generally, I prefer arrays when size is known, and `List`s when it is not. When data volume is high and/or memory usage a concern, I load into an `ArrayList` and then convert into an array with `aList.toArray()`. At some point I also had a small class that combined characteristics of ArrayList and LinkedList for a memory efficiency better than `ArrayList`. – Mario Rossi Aug 10 '13 at 08:17