determining size of public static float[][] by mxj arguments
i have the following code in which the size of the public static float[][] glodMat1 should be determined by the arguments in the mxj object.
for some reason the size of globMat1 stays 0 x 0.
what am i doing wrong? or should i determine globMat1's size in another way?
import com.cycling74.max.*;
public class ynsMf_mat extends MaxObject {
private static int rows = 0;
private static int cols = 0;
public ynsMf_mat(int a1, int a2) {
rows = a1;
cols = a2;
declareIO(2,1);
post(rows + " rows");
post(cols + " cols");
}
public static float[][] globMat1 = new float[rows][cols];
public void mat(int matInd) {
outlet(1, globMat1[matInd]);
}
public void matSize(int matInd) {
outlet(1, globMat1[matInd].length);
}
}
All code outside of your methods gets called when the object is instantiated, before the constructor is called. In the case of static code, it is only called the first time the class is loaded.
In short, at the time you assign new float[rows][cols]
to globMat1, the constructor has only been declared, not called. rows and cols are still 0.
It looks to me like you:
don't want globMat1 to be static
don't need rows and cols as variables outside of your constructor at all
need to instantiate globMat1 inside your constructor
If you really want globMat1 to be static (which would mean that no matter how many of these objects you make in max, there will only be ONE of them), you are better off making the object take a message that re-initializes that static member:
import com.cycling74.max.*;
public class ynsMf_mat extends MaxObject {
public static float[][] globMat1;
public ynsMf_mat() {
declareIO(2,1);
}
public void remakeMat(int rows, int cols) {
globMat1 = new float[rows][cols];
}
public void mat(int matInd) {
outlet(1, globMat1[matInd]);
}
public void matSize(int matInd) {
outlet(1, globMat1[matInd].length);
}
}
This would re-allocate the array whenever you send the appropriate message to any instance of this object. Note that in "normal" java code, remakeMat would be properly declared static, but here it can't be in order to be called directly from Max.
EDIT: i just saw your other posts. i guess you do want globMat1 to be static.
thanks for your suggestion, it works! i solved it like this to get the arguments determining teh size of the float[][]:
import com.cycling74.max.*;
public class ynsMf_mat extends MaxObject {
public static float[][] globMat1;
public ynsMf_mat(int rows, int cols) {
declareIO(2,1);
globMat1 = new float[rows][cols];
}
public void mat(int matInd) {
outlet(1, globMat1[matInd]);
}
public void matRows() {
outlet(1, globMat1.length);
}
public void matCols() {
outlet(1, globMat1[1].length);
}
}
but now the mxj objects which extend this class to refer to the global variable can't find the class anymore when i use arguments in the class above, when i remove the arguments it's no problem. her is the code for the class which extends the class above, could you tell me what i'm doing wrong?
import com.cycling74.max.*;
public class ynsMf_rec extends ynsMf_mat {
public ynsMf_rec() {
declareIO(2,1);
}
public void fillMat() {
float item = 1f;
for(int i1=0; i1
for(int i2=0; i2
globMat1[i1][i2] = item;
item = item + 1;
}
}
}
}
Short version: don't make the change to my code that you made. You are using a static member, but initializing it every time you instantiate a new instance of the class. This makes no sense. If you want a global, static member, use the code that i gave you and send the appropriate message to one of the mxj objects when you want to initialize it.
Unless I'm mistaken, the code you supplied here shouldn't even compile. Your parent class has only one constructor and it takes 2 parameters. Your child class' constructor doesn't explicitly call the super class' constructor, so it tries to call super(). This will fail because super() doesn't exist, only super(int,int).
If you do not explictly make a call to super
in your objects constructor, java automatically calls super()
before executing any of the code in your constructor. So your constructor actually is doing this:
public ynsMf_rec() {
super();
declareIO(2,1);
}
So if you need to call public ynsMf_mat(int a1, int a2)
from ynsMf_rec()
you must do so explictly:
public ynsMf_rec() {
super(20, 45);
declareIO(2,1);
}
or
public ynsMf_rec(int a1, int a2) {
super(a1, a2);
declareIO(2,1);
}
Or something along those lines.
I was thinking of mentioning a couple of other thoughts on this subject, but first could you elaborate a little more about what you are trying to accomplish? I have a feeling you want some object to have typed in arguments to set the array size and other objects to have no arguments? If that is going on or something completely different is there may be some other things to consider (I'd elaborate now, but am really tired and it would take a lot of energy to share these thoughts clearly and I may be answering off topic questions).
In the meantime, if anyone else wants to weigh in, I'm thinking designing a singleton class to store float[][] globMat1
would make a little more sense. While subclassing to share global data does feel like a very Java way to handle it, it also feels lind of like a hack to me since (if I am imagining yns' needs correctly) it doesn't seem like the children of ynsMf_mat would satisfy an "is a" relationship and are only subclassing to share data. I'd be happy to post a singleton example tomorrow.
Hmm, now I am also wondering if you could just put float[][] globMat1
in its own class and access it with static methods instead of a true singleton object would probably be even better and more Javaie.
Short version: don't make the change to my code that you made. You are using a static member, but initializing it every time you instantiate a new instance of the class. This makes no sense. If you want a global, static member, use the code that i gave you and send the appropriate message to one of the mxj objects when you want to initialize it.
This is one of the areas I was avoiding for now in my posts. I have a feeling yns chose to make that modification so that initialization of the static member can happen when the [mxj] loads instead of requiring a message to be sent. If that is what you want yns (because the issue with your code mentioned above is true) there are other ways to acheive that.
Unless I'm mistaken, the code you supplied here shouldn't even compile. Your parent class has only one constructor and it takes 2 parameters. Your child class' constructor doesn't explicitly call the super class' constructor, so it tries to call super(). This will fail because super() doesn't exist, only super(int,int).
It compiles because a super()
call to ynsMf_mat
just turns into a call to super()
in MaxObject
.
It compiles because a super() call to ynsMf_mat just turns into a call to super() in MaxObject.
No it doesn't. When I was looking at the code assuming it compiled, I too thought "Oh, will Java just call the MaxObject() default constructor? Okay." But if you think about it, it makes no sense, and if you try it, you'll find that it doesn't compile.
It compiles because a super() call to ynsMf_mat just turns into a call to super() in MaxObject.
No it doesn't. When I was looking at the code assuming it compiled, I too thought "Oh, will Java just call the MaxObject() default constructor? Okay." But if you think about it, it makes no sense, and if you try it, you'll find that it doesn't compile.
OOPS! Thanks for correcting that, ignore my post above.
I'd recommend writing something like this:
YNSMf_mat.java
public class YNSMf_mat {
private static float[][] globMat1;
public static void initialize(int rows, int cols) {
globMat1 = new float[rows][cols];
}
public static float[] mat(int matInd) {
return globMat1[matInd];
}
public static int matRows() {
return globMat1.length;
}
public static int matCols() {
return globMat1[1].length;
}
public static float getCell(int x, int y) {
return globMat1[x][y];
}
public static void setCell(int x, int y, float val) {
globMat1[x][y] = val;
}
}
and use it like this:
ynsMf_rec.java
import com.cycling74.max.*;
public class ynsMf_rec extends MaxObject {
public ynsMf_rec() {
YNSMf_mat.initialize(30, 10);
declareIO(2,1);
}
public void fillMat() {
float item = 1f;
for(int i1=0; i1
N.B.:This example won't work that well in practice because every [mxj ynsMf_rec]
you make will reinitialize the float[][]
in YNSMf_mat
. Also, there is nothing done to handle problems if you try to call a method of YNSMf_mat
before you call YNSMf_mat.initialize()
. I can share some thoughts on this, but like I mentioned last night, need to know a little more about your goals to really be helpful.
Thanks for all your suggestions!
I used and modified your last proposal Roth. Thanks for that! There are still some things I would like to add to my objects which I can't figure out though. I'll try to explain what I would like to make:
In the end I'm aiming for a set of objects that share a couple of matrices,. I would like to have an object that can store lists, one that can read them and one or more that can analyze the lists. Those operations at them selfs are not the problem, I already programmed them into another object, all together in one object.
The thing is that I would like to have some kind of system which works like MSP's buffer system. It would be nice if I could determine the name of the list that the object refers to by an argument, just like it works with MSP buffers. I find a way to this by creating 10 float[][]'s and letting the first argument of the object (an int) determine the float[][] to refer to. The code I used to this is in the bottom of this post. It works but it requires a lot of code and therefore doesn't seem very efficient to me, I might be wrong off course. Besides that it would be nice to have an unlimited amount of float[][]'s that can be created instead of being limited by how much float[][]'s i programmed into the object. And it would be nice to refer by names (strings) instead of numbers.
Another thing that would be nice but might be impossible is to let the number of arguments determine the number of dimensions of the matrix. So if there are (besides the "refer-to" argument) 2 arguments there would a float[][], when there are 3 arguments a float[][][] would be created and so on. I have no clue how I could do this or if it's even possible.
Here is the adjustment I made to Roth's code for the matrix storage class:
public class ynsMf_mat {
private static float[][] globMat1;
private static float[][] globMat2;
private static float[][] globMat3;
private static float[][] globMat4;
private static float[][] globMat5;
private static float[][] globMat6;
private static float[][] globMat7;
private static float[][] globMat8;
private static float[][] globMat9;
private static float[][] globMat10;
public static void init(int matInd, int rows, int cols) {
if (matInd == 1) {
globMat1 = new float[rows][cols];
} else if (matInd == 2) {
globMat2 = new float[rows][cols];
} else if (matInd == 3) {
globMat3 = new float[rows][cols];
} else if (matInd == 4) {
globMat4 = new float[rows][cols];
} else if (matInd == 5) {
globMat5 = new float[rows][cols];
} else if (matInd == 6) {
globMat6 = new float[rows][cols];
} else if (matInd == 7) {
globMat7 = new float[rows][cols];
} else if (matInd == 8) {
globMat8 = new float[rows][cols];
} else if (matInd == 9) {
globMat9 = new float[rows][cols];
} else if (matInd == 10) {
globMat10 = new float[rows][cols];
}
}
public static float[] matRow(int matInd, int row) {
if (matInd == 1) {
return globMat1[row];
} else if (matInd == 2) {
return globMat2[row];
} else if (matInd == 3) {
return globMat3[row];
} else if (matInd == 4) {
return globMat4[row];
} else if (matInd == 5) {
return globMat5[row];
} else if (matInd == 6) {
return globMat6[row];
} else if (matInd == 7) {
return globMat7[row];
} else if (matInd == 8) {
return globMat8[row];
} else if (matInd == 9) {
return globMat9[row];
} else {
return globMat10[row];
}
}
public static int matRows(int matInd) {
if (matInd == 1) {
return globMat1.length;
} else if (matInd == 2) {
return globMat2.length;
} else if (matInd == 3) {
return globMat3.length;
} else if (matInd == 4) {
return globMat4.length;
} else if (matInd == 5) {
return globMat5.length;
} else if (matInd == 6) {
return globMat6.length;
} else if (matInd == 7) {
return globMat7.length;
} else if (matInd == 8) {
return globMat8.length;
} else if (matInd == 9) {
return globMat9.length;
} else {
return globMat10.length;
}
}
public static int matCols(int matInd) {
if (matInd == 1) {
return globMat1[1].length;
} else if (matInd == 2) {
return globMat2[1].length;
} else if (matInd == 3) {
return globMat3[1].length;
} else if (matInd == 4) {
return globMat4[1].length;
} else if (matInd == 5) {
return globMat5[1].length;
} else if (matInd == 6) {
return globMat6[1].length;
} else if (matInd == 7) {
return globMat7[1].length;
} else if (matInd == 8) {
return globMat8[1].length;
} else if (matInd == 9) {
return globMat9[1].length;
} else {
return globMat10[1].length;
}
}
public static float getCell(int matInd, int x, int y) {
if (matInd == 1) {
return globMat1[x][y];
} else if (matInd == 2) {
return globMat2[x][y];
} else if (matInd == 3) {
return globMat3[x][y];
} else if (matInd == 4) {
return globMat4[x][y];
} else if (matInd == 5) {
return globMat5[x][y];
} else if (matInd == 6) {
return globMat6[x][y];
} else if (matInd == 7) {
return globMat7[x][y];
} else if (matInd == 8) {
return globMat8[x][y];
} else if (matInd == 9) {
return globMat9[x][y];
} else {
return globMat10[x][y];
}
}
public static void setCell(int matInd, int x, int y, float val) {
if (matInd == 1) {
globMat1[x][y] = val;
} else if (matInd == 2) {
globMat2[x][y] = val;
} else if (matInd == 3) {
globMat3[x][y] = val;
} else if (matInd == 4) {
globMat4[x][y] = val;
} else if (matInd == 5) {
globMat5[x][y] = val;
} else if (matInd == 6) {
globMat6[x][y] = val;
} else if (matInd == 7) {
globMat7[x][y] = val;
} else if (matInd == 8) {
globMat8[x][y] = val;
} else if (matInd == 9) {
globMat9[x][y] = val;
} else if (matInd == 10) {
globMat10[x][y] = val;
}
}
}
And here is the code I used for the "record" object:
import com.cycling74.max.*;
public class ynsMf_rec extends MaxObject {
private int matIndex;
public ynsMf_rec(int matInd, int rows, int cols) {
ynsMf_mat.init(matInd, rows, cols);
matIndex = matInd;
declareIO(2,1);
}
public void matRow(int row) {
outlet(1, ynsMf_mat.matRow(matIndex, row));
}
public void matRows() {
outlet(1, ynsMf_mat.matRows(matIndex));
}
public void matCols() {
outlet(1, ynsMf_mat.matCols(matIndex));
}
public void fillMat() {
float item = 1f;
for(int i1=0; i1
for(int i2=0; i2
ynsMf_mat.setCell(matIndex, i1, i2, item);
item = item + 1;
}
}
}
}
You want to do things that basically mean you don't want to use arrays. For some unspecified number of matrices, I would suggest looking at the java.util.ArrayList class.
import java.util.ArrayList;
public class ynsMf_mat {
private static ArrayList matrices = new ArrayList();
public static void init(int numMatrices, int rows, int cols) {
for (int i = 0; i != numMatrices; i++) {
matrices.add(new float[rows][cols]);
}
}
public static float[] matRow(int matInd, int row) {
return matrices.get(matInd)[row];
}
public static int matRows(int matInd) {
return matrices.get(matInd).length;
}
public static int matCols(int matInd) {
return matrices.get(matInd)[0].length;
}
public static float getCell(int matInd, int x, int y) {
return matrices.get(matInd)[x][y];
}
public static void setCell(int matInd, int x, int y, float val) {
matrices.get(matInd)[x][y] = val;
}
}
Although to be frank, I find this encapsulation of every operation on an array to be kind of ridiculous.
If you really want n-dimensional matrices, and you're working in Max, using Jitter is probably the way to go.
You may want to check out using HashTable instead of ArrayList since that will allow you to use strings as keys.
If you really want n-dimensional matrices, and you're working in Max, using Jitter is probably the way to go.
I kind of agree. If you weren't aware, Jitter is integrated with the Max Java SDK really nicely and so you may be better off using Jitter for sharing matrix data between your [mxj]s. I guess it is up to you to look at the advantages of using Jitter or your own custom data structure (I've been thinking about a similar issue recently with some fft externals I'm writing in C.
You may want to check out using HashTable instead of ArrayList since that will allow you to use strings as keys.
If you actually want a map, you almost definitely want HashMap and not Hashtable.
Thanks for your reply's. Brain, what your suggesting seems quite nice and a good solution, but when I saw it I figured it would be nice for the float[][]'s themselves to be of a variable size. The float[][] is there to store a list of lists. It would be nice if a list would be added to the list of lists and thus change the size of the list of lists. This so later on (in another object) I can loop trough the list.
The thing is that I have no clue to do this with ArrayList, Hashtable or HashMap since I'm quite new to Java. Since I am not really familiar with Jitter this kind of frightens me.
I should add that efficiency (Memory but especially CPU wise) is very very important to me. I'm willing to do some concessions when it comes tot the convenience of the objects since the writing and reading will have to happen at intervals up to 1 ms.
The thing is that I have no clue to do this with ArrayList, Hashtable or HashMap since I'm quite new to Java.
Google is your friend. All of the standard Java libraries are fully and publicly documented http://download.oracle.com/javase/1.5.0/docs/api/
If you want to change the size of a list at runtime, do not use an array. An array has a fixed size. Use ArrayList or some other java collection with a variable size.
I should add that efficiency (Memory but especially CPU wise) is very very important to me.
Trying to optimize for performance before you've identified bottlenecks is silly. Performance and "convenience" are not necessarily contradictory.
/brian
Well I figured there could be a difference in CPU usage in for instance, writing lists into a float[][] or into an ArrayList because an ArrayList needs to dynamically add a list in contrast to a float[][] which only needs to fill a list.
Also indexing lists by strings sounds like it would needs more CPU than indexing them by integers. I might be totally wrong, but if there are more things like these I'd love to know them in advance.
Well I figured there could be a difference in CPU usage in for instance, writing lists into a float[][] or into an ArrayList because an ArrayList needs to dynamically add a list in contrast to a float[][] which only needs to fill a list.
An ArrayList, as its name implies, is backed by an array. Yes, it is slow when it has to dynamically get bigger, because it has to allocate a new array and copy everything into it. This can be mitigated by pre-allocating the size if you have some idea beforehand. The tradeoff is that you will have wasted a bunch of memory if you overestimate.
But here's the thing: the people who wrote ArrayList are better java programmers than you and I put together, and the code has been used and hammered on by literally millions of Java programmers for over a decade. Their dynamically-sized array code is almost definitely going to be far better than whatever you and I write. So if a dynamically-allocated array is what you want, use the standard library.
Each and every data structure is more performant for certain operations than others; everything is a tradeoff. Knowing which data structures and which algorithms to use under what circumstances basically means having an understanding of the analysis of data structures and algorithms. One of the nice things about the Java standard library is the Collections API, which lets you write to a standard API and then switch out the underlying data structure fairly easily. Doing things this way does require an understanding of how Java interfaces work, though that's not terribly hard.
Also indexing lists by strings sounds like it would needs more CPU than indexing them by integers.
This is almost definitely true in the general case, but it would still be a constant-time lookup. And it might not matter depending on when the lookup happens.
I might be totally wrong, but if there are more things like these I'd love to know them in advance.
That's no small wish. I have an introductory book on algorithms you could start with; it's 1073 pages :)
/brian
I haven't been working on the objects for a while but since two I'm trying your suggestions but I'm failing to get it working.
I'm trying to refer to the matrices by strings with the HashMap class. I want the rows of the matrices to be a list of a list of floats with a variable length and one double value to store a value coming from MaxClock.getTime().
For this special kind of list I made the following class which seems to work fine:
class ListWrap {
public Atom[] datList;
public double deltTime;
public ListWrap(Atom[] dL, double dT) {
datList = dL;
deltTime = dT;
}
}
I would like the number of rows for the matrices to be variable so I figured I could make a HashMap containing ArratList's of the ListWrapper class above:
import com.cycling74.max.*;
import java.util.*;
public class ynsMFstor {
private static HashMap globMats = new HashMap();
public static void init(String matName) {
globMats.put(matName, new ArrayList());
}
}
Here Eclipse already gives some error:
Multiple markers at this line
- Type safety: The method put(Object, Object) belongs to the raw type HashMap. References to generic
type HashMap should be parameterized
- Line breakpoint:ynsMFstor [line: 9] - init(String)
I don't know if this error is crucial but I tried to make some of the functions I had for the float[][] and can't figure out how to get a value or a row out of the ArrayList() or how to get a value in there. For instance when I try:
public static ListWrap matRow(String matName, int row) {
return globMats.get(matName).get(row).datList;
}
I get the error:
The method get(int) is undefined for the type
Object
I'm probably misunderstanding some things, could anybody point me into the right direction please?
you need to parametize your HashMap the same way you parametized the ArrayList, with the angle brackets.
Thank you very very very much for your help! StupidI didn't realize that immediately. It works like charm now.
But yes, again I have something I'm struggling with for some time already and can't seem to figure out what is wrong. I created a new object to "record" lists into one of HashMap's matrices. It seems to me that this object is not able to access to the "global variable" HashMap.
My system now consists of 2 objects and a class for storing the matrices and doing operations on them. (I'm trying to built a system which works similar to MSP's buffer-system). The storage object in combination with the 1st object (which is an object that instantiates a matrix and can output some info about that matrix) work fine together. The third object I just made doesn't seem to be able to access the global variable.
I'll post the code for the objects at the bottom of this post. The error occurs in multiple parts of the 3rd object but seem to come from the same problem. For instance:
when i send the max message "record 1" in the left inlet of the object I get in the max-window:
java.lang.NullPointerException
at ynsMFstor.clearMat(ynsMFstor.java:32)
at ynsMFrec.record(ynsMFrec.java:20)
And when I send a list into the right inlet after the "record 1" message:
java.lang.NullPointerException
at ynsMFstor.clearMat(ynsMFstor.java:32)
at ynsMFrec.list(ynsMFrec.java:38)
The code for the storage object:
import java.util.*;
public class ynsMFstor {
private static HashMap> globMats = new HashMap>();
public static void init(String matName) {
globMats.put(matName, new ArrayList());
}
public static float[] matRow(String matName, int row) {
return globMats.get(matName).get(row).datList;
}
public static int matRows(String matName) {
return globMats.get(matName).size();
}
public static int matCols(String matName) {
return globMats.get(matName).get(1).datList.length;
}
public static float getCell(String matName, int x, int y) {
return globMats.get(matName).get(x).datList[y];
}
public static void addList(String matName, float[] data, double deltaTime) {
globMats.get(matName).add(new ListWrap(data, deltaTime));
}
public static void clearMat (String matName) {
globMats.get(matName).clear();
}
}
class ListWrap { public float[] datList;
public double deltTime;
public ListWrap(float[] dL, double dT) {
datList = dL;
deltTime = dT;
}
}
And here is the code for the instantiation object:
import com.cycling74.max.*;
public class ynsMFmat extends MaxObject {
private String matName;
public ynsMFmat(String name) {
ynsMFstor.init(name);
matName = name;
declareIO(2,1);
}
public void matRow(int row) {
outlet(1, ynsMFstor.matRow(matName, row));
}
public void matRows() {
outlet(1, ynsMFstor.matRows(matName));
}
public void matCols() {
outlet(1, ynsMFstor.matCols(matName));
}
public void fillMat(int rows) {
float[] dummyList = {1, 2, 3, 4, 5};
for(int i=0; i
ynsMFstor.addList(matName, dummyList, (double)i);
}
}
public void clearMat() {
ynsMFstor.clearMat(matName);
}
}
And the object which causes the error:
import com.cycling74.max.*;
public class ynsMFrec extends MaxObject {
private String matName;
private boolean rec = false;
private double time = -1;
private double endTime = 0;
public ynsMFrec(String name) {
matName = name;
declareIO(2,1);
}
public void record(boolean b) {
if(getInlet() == 0) {
rec = b;
if(b) {
time = -1;
ynsMFstor.clearMat(matName);
} else {
double curTime = MaxClock.getTime();
endTime = curTime - time;
}
}
}
public void matRow(int row) {
outlet(1, ynsMFstor.matRow(matName, row));
}
public void list(float[] data) {
if(getInlet() == 1 && rec) {
double deltaTime;
double curTime = MaxClock.getTime();
if(time == -1) {
deltaTime = 0;
ynsMFstor.clearMat(matName);
} else {
deltaTime = curTime - time;
}
ynsMFstor.addList(matName, data, deltaTime);
time = curTime;
}
}
}
I've just opened the patch to look at the problem with a fresh mind and for some reason everything works fine. I must have been tired when I tested it before posting the post above.
Again thanks for all the help I've got in this topic!
for some reason everything works fine.
If i understand well all this, I think it's working because you relaunched max, right ?
If you reedit/recompile one of your classes, it will be loaded in a different classloader by max, and then will not share your static data anymore, and then the errors should come back. (But maybe it's not a problem for you if you only need to edit your classes very rarely, then you just have to restart max.)
This is again the problem we are speaking about in that topic : https://cycling74.com/forums/various-mxj-objects-sharing-data-from-unique-java-array-static-not-working
(and the Hans's solution works!)
You're right. I have to restart Max when I've edited the one of the classes. I was going to post the problem in this topic but got distracted lately. And I don't see it as a big problem but it would be nice to be fixed. I'll try out your solution (if I understand it well).
Thanks for your feedback.