I'm sitting on an assignment for university and I'm at a point, where I fear I haven't really understood something fundamental in the concecpt of Java or OOP altogether. I'll try to make it as short as possible (maybe it's sufficient to just look at the 3rd code segment, but I just wanted to make sure, I included enough detail). I am to write a little employee management. One class within this project is the employeeManagement itself and this class should possess a method for sorting employees by first letter via bubblesort.
I have written 3 classes for this: The first one is "Employee", which contains a name and an ID (a running number) , getter and setter methods and one method for checking whether the first letter of one employee is smaller (lower in the alphabet) than the other. It looks like this:
static boolean isSmaller(Employee source, Employee target) {
char[] sourceArray = new char[source.name.length()];
char[] targetArray = new char[target.name.length()];
sourceArray = source.name.toCharArray();
targetArray = target.name.toCharArray();
if(sourceArray[0] < targetArray[0])
return true;
else
return false;
}
I tested it and it seems to work for my case. Now there's another class called EmployeeList and it manages the employees via an array of employees ("Employee" objects). The size of this array is determined via constructor. My code looks like this:
public class EmployeeList {
/*attributes*/
private int size;
private Employee[] employeeArray;
/* constructor */
public EmployeeList(int size) {
this.employeeArray = new Employee[size];
}
/* methods */
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
/* adds employee to end of the list. Returns false, if list is too small */
boolean add(Employee m) {
int id = m.getID();
if (id > employeeArray.length) {
return false;
} else {
employeeArray[id] = m;
return true;
}
}
/* returns employee at certain position */
Employee get(int index) {
return employeeArray[index];
}
/* Sets employee at certain position. Returns null, if position doesn't exist. Else returns old value. */
Employee set(int index, Employee m) {
if (employeeArray[index] == null) {
return null;
} else {
Employee before = employeeArray[index];
employeeArray[index] = m;
return before;
}
}
Now comes my real problem: In a third class called "employeeManagement" I am supposed to implement the sorting algorithm. The class looks like this:
public class EmployeeManagement {
private EmployeeList ml = new EmployeeList(3);
public boolean addEmployee(Employee e) {
return ml.add(e);
}
public void sortEmployee() {
System.out.println(ml.getSize()); // I wrote this for debugging, exactly here lies my problem
for (int n = ml.getSize(); n > 1; n--) {
for (int i = 0; i < n - 1; i++) {
if (Employee.isSmaller(ml.get(i), ml.get(i + 1)) == false) {
Employee old = ml.set(i, ml.get(i + 1));
ml.set(i+1, old);
}
}
}
}
The "println" before my comment returns "0" in console... I am expecting "3" as this is the size I gave the "EmployeeList" as parameter of the constructor within my "EmployeeManagement" class. Where is my mistake ? And how can I access the size of the object I created in the "EmployeeManagement" class (the "3") ? I'm really looking forward to your answers!
Thanks,
Phreneticus
You are not storing size in your constructor. Something like,
public EmployeeList(int size) {
this.employeeArray = new Employee[size];
this.size = size; // <-- add this.
}
Also, setSize isn't going to automatically copy (and grow) the array. You will need to copy the array, because Java arrays have a fixed length. Finally, you don't really need size here since employeeArray has a length.
The size variable you are calling is the class field. If you take a quick look at your code, the getter is getting the field (which is initialized as zero when created). The size you are using it. The good way of doing it would be to get the size of the array in the getter like this:
public int getSize() {
return employeeArray.length;
}
This would return the size of the array in the object.
Related
So lets say I have a class BaseballCard that creates a baseball card.
Now I need to make another class which would be my collection class.
For example I would call it BaseballCardCollection
and then I want to create methods like
size (which returns the numbers of cards in the collection)
addCard(adds a baseball object to the collection object)
removeCard (removes a baseball card)
and so on
What would be the best way to do this. I tried doing this
public CardCollectionList() {
BaseballCard[] baseballCardList = new BaseballCard[101];
}
So each object is insinuated with an array of type BaseballCard of size 100.
And then for example the size method I tried something like this
public int size(){
int size = 0;
for(int i = 1; i<this.baseballCardList.length; i++)
if (baseballCardList!= null)
size+=1;
}
But it doesn't work because "baseballCardList cannot be resolved to a variable"
You could try using ArrayLists - http://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html:
ArrayList<baseballCard> baseballCardList = new ArrayList<baseballCard>(0);
public boolean addCard(baseballCard card){
return baseballCardList.add(card);
}
public boolean removeCard(int card){
return baseballCardList.remove(card);
}
public baseballCard getCard(int card){
return baseballCardList.get(card);
}
public int sizeBaseballCardList(){
return baseballCardList.size();
}
public ArrayList<baseballCard> getBaseballCardList(){
return baseballCardList;
}
Move the variable BaseballCard[] baseballCardList outside the constructor, make it a field in your class. Do similar with size.
This is how the class should look like:
public class CardCollectionList {
//fields
private BaseballCard[] baseballCardList;
private int size;
//constructor
public CardCollectionList() {
baseballCardList = new BaseballCard[101];
}
//method
public int getSize() {
return this.size;
}
}
You could try creating your own class implementing the Collection interface and define your own methods + implement Collection methods:
public class myContainer implements Collection <BaseballCard> {
}
You need to move the variable declaration from the constructor to the class, so you can access it in other methods, too.
class CardCollectionList {
BaseballCard[] baseballCardList;
public CardCollectionList() {
baseballCardList = new BaseballCard[101];
}
public int size(){
int size = 0;
for(int i = 1; i<this.baseballCardList.length; i++) {
if (baseballCardList[i] != null) {
size+=1;
}
}
return size;
}
}
The code is as close to your fragment as possible. There are several ways to improve this (keep track of the size when adding, automatic array reallocation etc.). But it is a start if you want to try this yourself.
Normally, you'd probably just use ArrayList<BaseballCard>.
Now I need to make another class which would be my collection class.
... What would be the best way to do this.
I don't have enough reputation to comment on your question, so I am going to assume that you just want to store BaseballCard objects in a Java Collection. The Java SDK offers a lot of options. Since you are asking about the "best" way to go then I would use one of those unless you need to add additional functionality .
if you don't find what you need from the Java SDK or just want to create your own Collection then follow the advice given by #michał-szydłowski above
My problem is this: I have an iterator class which is supposed to iterate through elements in a given data structure, <E> let's say, but what I have managed to accomplish is that when I pass in the data structure it will iterate the data structure itself.
ie. DynamicIterator it = new DynamicIterator(da);
say da is an array the output will be [1,2,3,4,5,6] instead of 1,2,3,4,5,6
My issue is, more than anything, understanding the generally accepted practice for dealing with this more than the issue itself.
edit for code:
public class X<E>
{
private final E[] rray;
private int currentIndex = 0;
public X(E... a)
{
//if the incoming array is null, don't start
if(a == null)
{
System.out.println("Array is null");
System.exit(1);
}
//set the temp array (rray) to the incoming array (a)
this.rray = a;
}
//hasNext element?
public boolean hasNext()
{
return rray.length > currentIndex;
}
//next element (depends on hasNext())
public E next()
{
if (!hasNext())
{
System.out.println("Element doesn't exist, done");
System.exit(1);
}
return rray[currentIndex++];
}
//return array
public E[] access()
{
return rray;
}
}
You won't be able to do this with a completely generic parameter <E> - how would you iterate through a Throwable, for example? What your class X does at the moment is accept any number of objects in its constructor, and then simply returns each of those objects in turn.
If you restricted the bounds of the objects passed in to implement e.g. Iterable, then you can actually start to "look inside" them and return their contents:
public class X<E> {
private final Iterator<E> it;
public X(Iterable<E> a) {
it = a.iterator();
}
public boolean hasNext() {
return it.hasNext();
}
public E next() {
return it.next();
}
}
Although this doesn't really accomplish anything different to just using a.iterator() directly instead of an instance of X...
I recently came across a very stupid (at least from my point of view) implementation inside Androids Parcel class.
Suppose I have a simple class like this
class Foo implements Parcelable{
private String[] bars;
//other members
public in describeContents(){
return 0;
}
public void writeToParcel(Parcel dest, int flags){
dest.writeStringArray(bars);
//parcel others
}
private Foo(Parcel source){
source.readStringArray(bars);
//unparcel other members
}
public static final Parcelable.Creator<Foo> CREATOR = new Parcelable.Creator<Foo>(){
public Foo createFromParcel(Parcel source){
return new Foo(source);
}
public Foo[] newArray(int size){
return new Foo[size];
}
};
}
Now, if I want to Parcel a Foo Object and bars is null I see no way to recover from this situation (exept of catching Exceptions of course). Here is the implementation of these two methods from Parcel:
public final void writeStringArray(String[] val) {
if (val != null) {
int N = val.length;
writeInt(N);
for (int i=0; i<N; i++) {
writeString(val[i]);
}
} else {
writeInt(-1);
}
}
public final void readStringArray(String[] val) {
int N = readInt();
if (N == val.length) {
for (int i=0; i<N; i++) {
val[i] = readString();
}
} else {
throw new RuntimeException("bad array lengths");
}
}
So writeStringArray is fine if I pass bars which are null. It just writes -1 to the Parcel. But How is the method readStringArray supposed to get used? If I pass bars inside (which of course is null) I will get a NullPointerException from val.length. If I create bars before like say bars = new String[???] I don't get any clue how big it should be. If the size doesn't match what was written inside I recieve a RuntimeException.
Why is readStringArray not aware of a result of -1 which gets written on null objects from writeStringArray and just returns?
The only way I see is to save the size of bars before I call writeStringArray(String[]) which makes this method kind of useless. It will also redundatly save the size of the Array twice (one time for me to remember, the second time from writeStringArray).
Does anyone know how these two methods are supposed to be used, as there is NO java-doc for them on top?
You should use Parcel.createStringArray() in your case.
I can't imagine a proper use-case for Parcel.readStringArray(String[] val) but in order to use it you have to know the exact size of array and manually allocate it.
It's not really clear from the (lack of) documentation but readStringArray() is to be used when the object already knows how to create the string array before calling this function; for example when it's statistically instanciated or it's size is known from another previously read value.
What you need here is to call the function createStringArray() instead.
Sorry if this is answered somewhere due to me missing something obvious, but I've been googling this for days now and it just doesn't seem to make any sense. I've got 3 years of experience in Javascript and am getting into Java now, so I'm not behind on the basic concepts of anything and such.
I'm using IntelliJ for this, but it fails to point out the problem. The communication (access rights and instantiations) between my classes is fine, the code syntax and variable types are as well, etc, so I really can't tell what it is.
I have a Data class, which just holds "read-only" data for the other classes to use.
public class Data {
// snip
public static int[][] specs = {
{6,1,6,40},
{5,2,5,30},
{5,3,4,40},
{4,4,3,60}
};
}
There's another class that has to read this data when it's initialized.
public class Soldier {
// snip
public int range;
public Soldier() {
int x = ...; // user input
range = Data.specs[x][1];
}
}
The specs array itself contains its data as defined (ie the array is not empty), x is valid as an index of the specs array (ie 0 <= x <= 3), its type is int and Test has read access to the specs array (all confirmed with debug output statements). And yet, when it tries to set the value of range (then and only then, at that exact point), I get the "Index out of bounds" error.
Can someone please tell me what's going wrong when trying to read the array? Or am I right in saying that this is really weird and I need to post the entire code?
Note: a small new test also shows that, if I change the code to first output a manually chosen value from the array and then set the value of range, the console prints the error statement (and exits the program) and follows it up by printing the manually picked value, but assigning the value and then asking to output range only throws the error... That makes absolutely no sense at all!
Edit: I've edited the code above. The class called Test is called Soldier in my code (I'm making a text-based game...). Below's the stack trace, if it's any good without the full code (which is way long). The basic structure of my program is this:
1) Boot contains the main method and instantiates a new Game
2) Game instantiates x Teams
3) each Team instantiates an Army
4) each Army instantiates x Soldiers
Each instance of the classes is set as an attribute of the instantiating class (public Army army; and an Army instantiation in the Team constructor, for example). It's essentially a cascade of constructors instantiating subsequent classes and assigning them as their attributes.
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
at Army.<init>(Army.java:13)
at Team.<init>(Team.java:19)
at Game.<init>(Game.java:22)
at Boot.main(Boot.java:15)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)5
Edit edit: here's the semi-full code (I'm leaving out the stuff that has absolutely nothing to do with it, including the imports). It's in no particular order and the classes are in separate .java files within the IntelliJ project. The game continues up to the point where a new Soldier asks for its type to be designated (the function performing the user input is working fine and validating the input as proven by a technically identical other part of the game).
public class Boot {
public static void main(String[] args) {
Object[] games = new Object[] {};
if (Lib.userConfirmPrompt("Start the game?") == true) {
do {
games[games.length] = new Game();
}
while (Lib.userConfirmPrompt("Do you want to play again?") == true);
}
System.exit(0);
}
}
public class Game {
public Object[] teams = new Object[] {};
public Game() {
for (int i = 0;i < settings.xbots + 1;i++) {
teams[teams.length] = new Team(this);
}
}
}
public class Team {
public Game game;
public Army army;
public Team(Game p) {
game = p;
army = new Army(this);
}
}
public class Army {
public Team team;
public static Object[] soldiers = new Object[] {};
public Army(Team p) {
team = p;
for (int i = 0;i < team.game.settings.xsoldiers;i++) {
soldiers[soldiers.length] = new Soldier(this);
}
}
}
public class Soldier {
private Army army;
public int sight;
public int range;
public int distance;
public int damage;
public Soldier(Army p) {
army = p;
int type = Lib.userTxtIntOptionsPrompt(Data.isoldiertypes);
// HERE is where it crashes, type is assigned and valid but the array access fails
sight = Data.isoldierspecs[type][0];
range = Data.isoldierspecs[type][1];
distance = Data.isoldierspecs[type][2];
damage = Data.isoldierspecs[type][3];
}
}
public class Data {
public static List isoldiertypes = Arrays.asList("Scout","Private","Machinegunner","Grenadier");
public static int[][] isoldierspecs = {
{6,1,6,40},
{5,2,5,30},
{5,3,4,40},
{4,4,3,60}
};
}
public class Lib {
private static Scanner input = new Scanner(System.in);
// output
// default: 1 query string to print
public static void outBase(String query) {
System.out.print(query);
}
public static void outStd(String query) {
outBase(query + "\n");
}
// end of output
// input
// default: 1 query string to print,
// query and input are in-line (exception: userConfirmPrompt prints query block-wise and default instruction in-line before input),
// keeps user hostage until valid input is given (exception: userPrompt returns blindly)
public static String userPrompt(String query) {
outBase(query);
return input.nextLine();
}
public static String userTxtPrompt(String query) {
String menuinput = null;
do {
if (menuinput != null) {
userHostage();
}
menuinput = userPrompt(query);
} while (menuinput.length() == 0);
return menuinput;
}
public static int userIntPrompt(String query) {
String menuinput = null;
do {
if (menuinput != null) {
userHostage();
}
menuinput = userTxtPrompt(query);
} while(menuinput.matches("^-?\\d+$") == false);
return new Integer(menuinput);
}
// end of input
// options input
// default: takes a List of options as argument,
// prints an enumerated list of these options string-wise,
// prompts for a numeral selection of the desired option and returns the number if valid
public static int userTxtIntOptionsPrompt(List options) {
int choice = 0;
Boolean chosen = false;
do {
if (chosen == true) {
userHostage();
} else {
chosen = true;
}
chosen = true;
for (int i = 0;i < options.size() - 2;i++) {
outStd((i + 1) + ") " + options.get(i) + ",");
}
outStd((options.size() - 1) + ") " + options.get(options.size() - 2) + "\nand " + options.size() + ") " + options.get(options.size() - 1) + ".");
choice = userIntPrompt("Enter the number of the option you'd like to select: ") - 1;
} while(choice < 0 || choice >= options.size());
return choice;
}
// end of options input
// miscellaneous
public static void userHostage() {
outStd("Invalid operation. Please try again.");
}
}
The problem is in your Army class:
public static Object[] soldiers = new Object[] {};
You initialize an empty (length == 0) array named soldiers, but later you access:
soldiers[soldiers.length] = new Soldier(this);
This causes the failure.
By definition, soldiers.length is out of the bound of the array (since the bound is from 0 to soldiers.length-1)
To overcome it - make sure you allocate enough space in the array soldiers or use a dynamic array (ArrayList) instead. You can append elements to an ArrayList using ArrayList.add(), and you don't need to know the expected size before filling it up.
The x should be greater than -1 and less than 4.
The stacktrace does not mention the Solder class, its in the conctructor of the Army class.
Any how, only knowing that the index should be within a range is not enough. As a programmer its your duty to validate the index before trying to access an element at that index.
if(index > 0 && index < array.length) {
//then only acess the element at index
Problem is the array soldiers is of size 0.
This line int x = ...; // user input implies that you are taking input in some fashion from the user and accessing the array with it. Are you checking this value to see that is in range (i.e., between 0 and 3)? If not, this may be why your testing works.
Edit: something like this might solve it for you:
public class Army {
public Team team;
public Vector<Soldier> soldiers;
public Army(Team p) {
soldiers = new Vector<Soldier>()
team = p;
for (int i = 0;i < team.game.settings.xsoldiers;i++) {
soldiers.add(new Soldier(this));
}
}
}
Judging by your other code, this sort of pattern will be useful in your Game object as well.
This code works perfectly, but unfortunately it triggers garbage collection because of the Arrays.sort() Comparator.
Is there a way to do this that won't trigger Garbage Collection?
(NOTE: This code has been modified to be more "generic". The actual code is for an Android game, which is why Garbage Collection-induced slowdown is an issue.)
static final byte INCOME = 0;
static final byte INDEX = 1;
public void vSortEmployees() {
nPaidEmployees = 0;
for (nIter=0; nIter<MAX_EMPLOYEES; nIter++) {
if ((employees[nIter].current == true) && (employees[nIter].volunteer == false)) {
// We have another current and paid employee; add that employee's "amount earned to date" to the list.
paidemployees[nPaidEmployees][INCOME] = employees[nIter].fGetTotalIncomeToDate();
paidemployees[nPaidEmployees][INDEX] = nIter;
nPaidEmployees++;
}
}
Arrays.sort(paidemployees, new Comparator<float[]>() {
#Override
public int compare(float[] f1, float[] f2) {
if (f2[INCOME] < f1[INCOME])
return -1;
else if (f2[INCOME] > f1[INCOME])
return 1;
else
return 0;
}
});
// Now we have a list of current, paid employees in order of income received.
// Highest income paid out
paidemployees[0][INCOME]
// Second highest income paid out
paidemployees[1][INCOME]
// If we need to reference the original employee object, we can:
employees[paidemployees[0][INDEX]].getName();
}
There is not way to consistently trigger or not to trigger GC. GC lives its own life. The fact that it runs when you are sorting your array does not mean anything.
But however you probably can do something. Just do not user anonymous inner class for comparator. You do not really need this. Use regular class and create its object as a singleton. Then just use this instance. In this case no new objects will be created in your code during the sort and GC probably will not run.
class FloatArrayComparator implements Comparator<float[]>() {
#Override
public int compare(float[] f1, float[] f2) {
if (f2[INCOME] < f1[INCOME])
return -1;
else if (f2[INCOME] > f1[INCOME])
return 1;
else
return 0;
}
};
class SomeClass {
private Comparator<float[]> floatArrayComparator = new FloatArrayComparator();
void myMethod() {
Arrays.sort(myArray, floatArrayComparator);
}
}