I create a middle class save many variables in it, example:
public static class Middle{
public static List<Student> listStudent = new ArrayList<>();
public static String level = 1; (this example of level of a character in the game)
}
And assign value for those variables
class A{
Middle.listStudent = GetData();
Middle.level++;
Intent intent = new Intent(A.this, B.class);
startActivity(intent)
}
And then in next class (or activity) we using those variables with new data
class B{
ShowResult(Middle.listStudent);
ShowResult(Middle.level);
}
I using this way because don't want to transfer data by Intent.
My question is, can we using this way too much in the whole application without getting any issue, and if the middle class it shut down for any reason, makes losing data?
If some static class become shutdown, maybe some serious error
occurs in your application.The JVM had to exit.
In multithreading environment,this way can cause dirty read and
brings into some strange thing.
You can try the code below. And see what's going on.
public static void main(String[] args) {
// create three threads to run it
for (int i = 0; i < 3; i++) {
//simulate multi-threaded environment
new Thread(() -> {
for (int j = 0; j < 10; j++) {
StaticData.listStudent.add(Thread.currentThread().getName() + ":" + j);
StaticData.level++;
}
}).start();
}
//show the last result , in single thread ,result must be 30 31 ,but maybe not this in multi-threaded environment
System.out.println("Total Result listStudent's size is :" + StaticData.listStudent.size());
System.out.println("Total Result level is :" + StaticData.level);
}
public static class StaticData {
public static List<String> listStudent = new ArrayList<>();
public static Integer level = 1;
}
Related
I'm bothered by the question whether the address of anonymous inner class in Java keep invariable.
I made some test codes,
public class AnonymousInnerClass {
public static void main(final String[] args) {
for (int i = 0; i < 5; i++) {
final Runnable x = () -> {
};
System.out.println(x);
}
for (int i = 0; i < 5; i++) {
final int j = i;
final Runnable x = () -> {
final int k = j;
System.out.println(k);
};
System.out.println(x);
}
}
}
and the outputs are not consistent.
main.AnonymousInnerClass$$Lambda$1/0x0000000800000a00#7a46a697
main.AnonymousInnerClass$$Lambda$1/0x0000000800000a00#7a46a697
main.AnonymousInnerClass$$Lambda$1/0x0000000800000a00#7a46a697
main.AnonymousInnerClass$$Lambda$1/0x0000000800000a00#7a46a697
main.AnonymousInnerClass$$Lambda$1/0x0000000800000a00#7a46a697
main.AnonymousInnerClass$$Lambda$2/0x0000000800000c18#532760d8
main.AnonymousInnerClass$$Lambda$2/0x0000000800000c18#57fa26b7
main.AnonymousInnerClass$$Lambda$2/0x0000000800000c18#5f8ed237
main.AnonymousInnerClass$$Lambda$2/0x0000000800000c18#2f410acf
main.AnonymousInnerClass$$Lambda$2/0x0000000800000c18#47089e5f
It seems the address is invariable if class is simple, I'm wondering that's compiler optimizing behavior or language specification.
More realistic scenario, I'm working with Android's MutableLiveData.
Suppose I have a variable passwd,
public final MutableLiveData<String> passwd = new MutableLiveData<>("");
and passwd has a method public void observe(#NonNull LifecycleOwner owner, #NonNull Observer<? super T> observer).
Normally I call it with an anonymous inner class passwd.observe(mLifecycleOwner, str -> doSomething());, and maybe call it several times.
Api doc said the call would be ignored if the observer has been always registered, that's what I need. But this relies on the address of str -> doSomething() would not change.
I don't want to extract it to a named variable, so I want to know if I can get a language specification guarantee that its address would not change, or if there are some conventions or patterns to handle this situation.
I am new to parallel stream and trying to make 1 sample program that will calculate value * 100(1 to 100) and store it in map.
While executing code I am getting different count on each iteration.
I may be wrong at somewhere so please guide me anyone knows the proper way to do so.
code:
import java.util.*;
import java.lang.*;
import java.io.*;
import java.util.stream.Collectors;
public class Main{
static int l = 0;
public static void main (String[] args) throws java.lang.Exception {
letsGoParallel();
}
public static int makeSomeMagic(int data) {
l++;
return data * 100;
}
public static void letsGoParallel() {
List<Integer> dataList = new ArrayList<>();
for(int i = 1; i <= 100 ; i++) {
dataList.add(i);
}
Map<Integer, Integer> resultMap = new HashMap<>();
dataList.parallelStream().map(f -> {
Integer xx = 0;
{
xx = makeSomeMagic(f);
}
resultMap.put(f, xx);
return 0;
}).collect(Collectors.toList());
System.out.println("Input Size: " + dataList.size());
System.out.println("Size: " + resultMap.size());
System.out.println("Function Called: " + l);
}
}
Runnable Code
Last Output
Input Size: 100
Size: 100
Function Called: 98
On each time run output differs.
I want to use parallel stream in my own application but due to this confusion/issue I can't.
In my application I have 100-200 unique numbers on which some same operation needs to be performed. In short there's function which process something.
Your access to both the HashMap and to the l variable are both not thread safe, which is why the output is different in each run.
The correct way to do what you are trying to do is collecting the Stream elements into a Map:
Map<Integer, Integer> resultMap =
dataList.parallelStream()
.collect(Collectors.toMap (Function.identity (), Main::makeSomeMagic));
EDIT: The l variable is still updated in a not thread safe way with this code, so you'll have to add your own thread safety if the final value of the variable is important to you.
By putting some values in resultMap you're using a side-effect:
dataList.parallelStream().map(f -> {
Integer xx = 0;
{
xx = makeSomeMagic(f);
}
resultMap.put(f, xx);
return 0;
})
The API states:
Stateless operations, such as filter and map, retain no state from
previously seen element when processing a new element -- each element
can be processed independently of operations on other elements.
Going on with:
Stream pipeline results may be nondeterministic or incorrect if the
behavioral parameters to the stream operations are stateful. A
stateful lambda (or other object implementing the appropriate
functional interface) is one whose result depends on any state which
might change during the execution of the stream pipeline.
It follows an example similar to yours showing:
... if the mapping operation is performed in parallel, the results for
the same input could vary from run to run, due to thread scheduling
differences, whereas, with a stateless lambda expression the results
would always be the same.
That explains your observation: On each time run output differs.
The right approach is shown by #Eran
Hopefully it works fine. by making Synchronied function makeSomeMagic and using Threadsafe data structure ConcurrentHashMap
and write simple statement
dataList.parallelStream().forEach(f -> resultMap.put(f, makeSomeMagic(f)));
Whole code is here :
import java.util.*;
import java.lang.*;
import java.io.*;
import java.util.stream.Collectors;
public class Main{
static int l = 0;
public static void main (String[] args) throws java.lang.Exception {
letsGoParallel();
}
public synchronized static int makeSomeMagic( int data) { // make it synchonized
l++;
return data * 100;
}
public static void letsGoParallel() {
List<Integer> dataList = new ArrayList<>();
for(int i = 1; i <= 100 ; i++) {
dataList.add(i);
}
Map<Integer, Integer> resultMap = new ConcurrentHashMap<>();// use ConcurrentHashMap
dataList.parallelStream().forEach(f -> resultMap.put(f, makeSomeMagic(f)));
System.out.println("Input Size: " + dataList.size());
System.out.println("Size: " + resultMap.size());
System.out.println("Function Called: " + l);
}
}
There is no need to count how many times the method invoked.
Stream will help you do loop in byte code.
Pass your logic(function) to Stream, do not use no thread-safe variable in multi-thread(include parallelStream)
like this.
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class ParallelStreamClient {
// static int l = 0;---> no need to count times.
public static void main(String[] args) throws java.lang.Exception {
letsGoParallel();
}
public static int makeSomeMagic(int data) {
// l++;-----> this is no thread-safe way
return data * 100;
}
public static void letsGoParallel() {
List<Integer> dataList = new ArrayList<>();
for (int i = 1; i <= 100; i++) {
dataList.add(i);
}
Map<Integer, Integer> resultMap =
dataList.parallelStream().collect(Collectors.toMap(i -> i,ParallelStreamClient::makeSomeMagic));
System.out.println("Input Size: " + dataList.size());
System.out.println("Size: " + resultMap.size());
//System.out.println("Function Called: " + l);
}
I'm using the following sample code to download a pojo that I found from this post:
import h2o
h2o.init()
iris_df = h2o.import_file("https://s3.amazonaws.com/h2o-public-test-data/smalldata/iris/iris.csv")
from h2o.estimators.glm import H2OGeneralizedLinearEstimator
predictors = iris_df.columns[0:4]
response_col = "C5"
train,valid,test = iris_df.split_frame([.7,.15], seed =1234)
glm_model = H2OGeneralizedLinearEstimator(family="multinomial")
glm_model.train(predictors, response_col, training_frame = train, validation_frame = valid)
h2o.download_pojo(glm_model, path = '/Users/your_user_name/Desktop/', get_jar = True)
When I open the downloaded java file I'm given some instructions for how to compile it. The following compiles successfully:
javac -cp h2o-genmodel.jar -J-Xmx2g -J-XX:MaxPermSize=128m GLM_model_python_1488677745392_2.java
Now, I'm not sure how to use it. I've tried the following:
java -cp h2o-genmodel.jar javac -cp h2o-genmodel.jar -J-Xmx2g -J-XX:MaxPermSize=128m GLM_model_python_1488677745392_2.java
The following is the code in the pojo:
/*
Licensed under the Apache License, Version 2.0
http://www.apache.org/licenses/LICENSE-2.0.html
AUTOGENERATED BY H2O at 2017-03-05T01:51:46.237Z
3.10.3.2
Standalone prediction code with sample test data for GLMModel named GLM_model_python_1488677745392_2
How to download, compile and execute:
mkdir tmpdir
cd tmpdir
curl http:/10.0.0.4/10.0.0.4:54321/3/h2o-genmodel.jar > h2o-genmodel.jar
curl http:/10.0.0.4/10.0.0.4:54321/3/Models.java/GLM_model_python_1488677745392_2 > GLM_model_python_1488677745392_2.java
javac -cp h2o-genmodel.jar -J-Xmx2g -J-XX:MaxPermSize=128m GLM_model_python_1488677745392_2.java
(Note: Try java argument -XX:+PrintCompilation to show runtime JIT compiler behavior.)
*/
import java.util.Map;
import hex.genmodel.GenModel;
import hex.genmodel.annotations.ModelPojo;
#ModelPojo(name="GLM_model_python_1488677745392_2", algorithm="glm")
public class GLM_model_python_1488677745392_2 extends GenModel {
public hex.ModelCategory getModelCategory() { return hex.ModelCategory.Multinomial; }
public boolean isSupervised() { return true; }
public int nfeatures() { return 4; }
public int nclasses() { return 3; }
// Names of columns used by model.
public static final String[] NAMES = NamesHolder_GLM_model_python_1488677745392_2.VALUES;
// Number of output classes included in training data response column.
public static final int NCLASSES = 3;
// Column domains. The last array contains domain of response column.
public static final String[][] DOMAINS = new String[][] {
/* C1 */ null,
/* C2 */ null,
/* C3 */ null,
/* C4 */ null,
/* C5 */ GLM_model_python_1488677745392_2_ColInfo_4.VALUES
};
// Prior class distribution
public static final double[] PRIOR_CLASS_DISTRIB = {0.2818181818181818,0.33636363636363636,0.38181818181818183};
// Class distribution used for model building
public static final double[] MODEL_CLASS_DISTRIB = null;
public GLM_model_python_1488677745392_2() { super(NAMES,DOMAINS); }
public String getUUID() { return Long.toString(-5598526670666235824L); }
// Pass in data in a double[], pre-aligned to the Model's requirements.
// Jam predictions into the preds[] array; preds[0] is reserved for the
// main prediction (class for classifiers or value for regression),
// and remaining columns hold a probability distribution for classifiers.
public final double[] score0( double[] data, double[] preds ) {
final double [] b = BETA.VALUES;
for(int i = 0; i < 0; ++i) if(Double.isNaN(data[i])) data[i] = CAT_MODES.VALUES[i];
for(int i = 0; i < 4; ++i) if(Double.isNaN(data[i + 0])) data[i+0] = NUM_MEANS.VALUES[i];
preds[0] = 0;
for(int c = 0; c < 3; ++c){
preds[c+1] = 0;
for(int i = 0; i < 4; ++i)
preds[c+1] += b[0+i + c*5]*data[i];
preds[c+1] += b[4 + c*5]; // reduce intercept
}
double max_row = 0;
for(int c = 1; c < preds.length; ++c) if(preds[c] > max_row) max_row = preds[c];
double sum_exp = 0;
for(int c = 1; c < preds.length; ++c) { sum_exp += (preds[c] = Math.exp(preds[c]-max_row));}
sum_exp = 1/sum_exp;
double max_p = 0;
for(int c = 1; c < preds.length; ++c) if((preds[c] *= sum_exp) > max_p){ max_p = preds[c]; preds[0] = c-1;};
return preds;
}
public static class BETA implements java.io.Serializable {
public static final double[] VALUES = new double[15];
static {
BETA_0.fill(VALUES);
}
static final class BETA_0 implements java.io.Serializable {
static final void fill(double[] sa) {
sa[0] = -1.4700470387418272;
sa[1] = 4.26067731522767;
sa[2] = -2.285756276489862;
sa[3] = -4.312931422791621;
sa[4] = 5.231215014401568;
sa[5] = 1.7769023115830205;
sa[6] = -0.2534145823550425;
sa[7] = -0.9887536067536575;
sa[8] = -1.2706135235877678;
sa[9] = -4.319817154759757;
sa[10] = 0.0;
sa[11] = -3.024835247270209;
sa[12] = 3.8622405283810464;
sa[13] = 7.018262604176258;
sa[14] = -22.702291637028203;
}
}
}
// Imputed numeric values
static class NUM_MEANS implements java.io.Serializable {
public static final double[] VALUES = new double[4];
static {
NUM_MEANS_0.fill(VALUES);
}
static final class NUM_MEANS_0 implements java.io.Serializable {
static final void fill(double[] sa) {
sa[0] = 5.90272727272727;
sa[1] = 3.024545454545454;
sa[2] = 3.9490909090909097;
sa[3] = 1.2872727272727267;
}
}
}
// Imputed categorical values.
static class CAT_MODES implements java.io.Serializable {
public static final int[] VALUES = new int[0];
static {
}
}
// Categorical Offsets
public static final int[] CATOFFS = {0};
}
// The class representing training column names
class NamesHolder_GLM_model_python_1488677745392_2 implements java.io.Serializable {
public static final String[] VALUES = new String[4];
static {
NamesHolder_GLM_model_python_1488677745392_2_0.fill(VALUES);
}
static final class NamesHolder_GLM_model_python_1488677745392_2_0 implements java.io.Serializable {
static final void fill(String[] sa) {
sa[0] = "C1";
sa[1] = "C2";
sa[2] = "C3";
sa[3] = "C4";
}
}
}
// The class representing column C5
class GLM_model_python_1488677745392_2_ColInfo_4 implements java.io.Serializable {
public static final String[] VALUES = new String[3];
static {
GLM_model_python_1488677745392_2_ColInfo_4_0.fill(VALUES);
}
static final class GLM_model_python_1488677745392_2_ColInfo_4_0 implements java.io.Serializable {
static final void fill(String[] sa) {
sa[0] = "Iris-setosa";
sa[1] = "Iris-versicolor";
sa[2] = "Iris-virginica";
}
}
}
Now, I think I need to call score0. I've figured out how to create my own main.java and create an entrypoint to main() so that I can instantiate the object and call score0, but I have no idea how it's supposed to work. I'm expecting to feed in 4 doubles and get back a category, but instead, the function takes two double[] and I can't figure out exactly what to put where and how to read the results. Here's my main:
public class Main {
public static void main(String[] args) {
double[] input = {4.6, 3.1, 1.5, 0.2};
double[] output = new double[4];
GLM_model_python_1488677745392_2 m = new GLM_model_python_1488677745392_2();
double[] t = m.score0(input,output);
for(int i = 0; i < t.length; i++) System.out.println(t[i]);
}
}
I'm actually getting a bunch of data returned, but I don't know what any of it means. I think I'm completely using the second argument incorrectly, but I'm not sure what to do. Here's the output:
0.0
0.9976588811416329
0.0023411188583572825
9.662837354438092E-15
A few points:
I don't recommend trying to call score0 directly when you are just learning how to use H2O POJOs. The EasyPredictModelWrapper API was created to provide a friendly interface, and I recommend using that if you can. (The best reason to skip the Easy API layer would be if you're interested in pure raw speed.)
You may find it easier to work with a MOJO instead of a POJO. MOJOs can be used in exactly the same way as POJOs, but they are data representations of the model rather than code representations of the model. MOJOs have strong backwards compatibility guarantees, and do not need to be compiled. I recommend using the MOJO if you can.
POJO and MOJO online documentation for the latest stable release of H2O can be found here:
http://docs.h2o.ai/h2o/latest-stable/h2o-genmodel/javadoc/index.html
Code for the EasyPredictModelWrapper can be found here:
https://github.com/h2oai/h2o-3/blob/master/h2o-genmodel/src/main/java/hex/genmodel/easy/EasyPredictModelWrapper.java
For people that really want to call score0 directly, the best documentation for how to do that is the EasyPredictModelWrapper code.
Here is a POJO usage code snippet from the documentation (of H2O version 3.10.4.1) of how to make a new prediction with the Easy API:
import java.io.*;
import hex.genmodel.easy.RowData;
import hex.genmodel.easy.EasyPredictModelWrapper;
import hex.genmodel.easy.prediction.*;
public class main {
private static String modelClassName = "gbm_pojo_test";
public static void main(String[] args) throws Exception {
hex.genmodel.GenModel rawModel;
rawModel = (hex.genmodel.GenModel) Class.forName(modelClassName).newInstance();
EasyPredictModelWrapper model = new EasyPredictModelWrapper(rawModel);
//
// By default, unknown categorical levels throw PredictUnknownCategoricalLevelException.
// Optionally configure the wrapper to treat unknown categorical levels as N/A instead
// and strings that cannot be converted to numbers also to N/As:
//
// EasyPredictModelWrapper model = new EasyPredictModelWrapper(
// new EasyPredictModelWrapper.Config()
// .setModel(rawModel)
// .setConvertUnknownCategoricalLevelsToNa(true)
// .setConvertInvalidNumbersToNa(true)
// );
RowData row = new RowData();
row.put("Year", "1987");
row.put("Month", "10");
row.put("DayofMonth", "14");
row.put("DayOfWeek", "3");
row.put("CRSDepTime", "730");
row.put("UniqueCarrier", "PS");
row.put("Origin", "SAN");
row.put("Dest", "SFO");
BinomialModelPrediction p = model.predictBinomial(row);
System.out.println("Label (aka prediction) is flight departure delayed: " + p.label);
System.out.print("Class probabilities: ");
for (int i = 0; i < p.classProbabilities.length; i++) {
if (i > 0) {
System.out.print(",");
}
System.out.print(p.classProbabilities[i]);
}
System.out.println("");
}
}
After stuffing a new prediction value into row (which is just a Map), you call predictBinomial() to make a prediction.
Almost exactly the same code can be used for a MOJO, except you need to instantiate the model from a data file instead of from a class. So instead of this code for POJO:
hex.genmodel.GenModel rawModel;
rawModel = (hex.genmodel.GenModel) Class.forName(modelClassName).newInstance();
EasyPredictModelWrapper model = new EasyPredictModelWrapper(rawModel);
you would have this code for MOJO:
EasyPredictModelWrapper model = new EasyPredictModelWrapper(MojoModel.load("GBM_model_R_1475248925871_74.zip"));
If anyone finds this thread, I found an even easier way to leverage the downloaded pojo. H2o steam handles it nicely:
~/steam-1.1.6-linux-amd64 > java -jar var/master/assets/jetty-runner.jar --port 8888 var/master/assets/ROOT.war &
curl -X POST --form pojo=#/home/tome/pojo/DL_defaults.java --form jar=#/home/tome/pojo/h2o-genmodel.jar localhost:8888/makewar > example.war
java -jar /home/tome/steam-1.1.6-linux-amd64/var/master/assets/jetty-runner.jar --port 7077 example.war
Then you can query it:
01:34:57 PS C:\dropbox\scripts> Invoke-RestMethod "http://notexist.eastus.cloudapp.azure.com:7077/predict?C1=4.6&C2=3.1&C3=1.5&C4=0.2"
labelIndex label classProbabilities
---------- ----- ------------------
0 Iris-setosa {0.9976588811416329, 0.0023411188583572825, 9.66283735443809E-15}
The main http: page provides a nice interface for building your query, and if you don't like the above, the full version of Steam provides a way to connect to H2O directly and do the download, conversion, and deployment of a model for you in a couple of clicks.
Never mind - it looks like I had it correct after all. The output refers to the 3 categories I have:
t[1] is setosa
t[2] is versicolor
t[3] is virginica
the 0.9976588811416329 refers to the percentage it thinks it's in that category. So the data is 99.8% setosa.
My current program is a simple game.. I am trying to make it to help figure out how to properly use OOP. But every time I get a little bit into a new program I run into the same problem:
I make an instance to use in my game. This instance could be anything - the main hero, a monster, whatever. Everything is going good, the instance is exactly what I want it to be. Maybe I manipulate it a little bit.
I then try to use a different class, I guess I create an instance of a different class, in an attempt to further manipulate the original instance. Maybe I originally created my hero and changed his stats, and now at this point I am trying to have my hero say something based on the stats that were chosen.
This point is always where I hit a roadblock. I can't seem to do what I want to do here - I can't use that original instance I made because It was just an instance and I don't know how to manipulate it(or even if I am supposed to) from within a new class.
So.. it's a fundamental design problem/lack of understanding of OOP.
But I feel like I have a decent grasp on how to actually program stuff.. I keep reading and reading and getting advice but I can't seem to get past this barrier.
I know people on this site don't seem to like posts like this but maybe someone here can Identify with what it is I am not grasping.
Here is an example of what I am talking about, the game I am trying to make right now.
package pkgnew.stupid.game;
import java.util.Scanner;
/**
*
* #author whyguy2
*/
public class Hero {
public static int heroattack = 1;
public static int herospeed = 1;
public static int heroarmorpen = 1;
public static int heroarmor = 1;
public static int herohealth = 5;
public static String inputstat;
public static int herothepoints = 0;
Hero(int points){
spendpoint(points);
}
public void attack(){
}
public void die(){
System.exit(0);
}
public void winbattle(){
System.out.println("You win the battle and have one new point to spend!");
spendpoint(1);
new NewEncounter();
}
public void levelup(){
System.out.println("You have leveled up and receive 5 new points to spend");
spendpoint(5);
new NewEncounter();
}
public void spendpoint(int points){
for(int x=0; x<points; x++){
System.out.println("Available Points: " + points);
System.out.println("Available Attributes:");
System.out.println("attack: " + heroattack);
System.out.println("speed: " + herospeed);
System.out.println("armorpen: " + heroarmorpen);
System.out.println("armor: " + heroarmor);
System.out.println("health: " + herohealth);
System.out.println(points);
System.out.println(x);
Scanner keyboard = new Scanner( System.in );
inputstat = keyboard.next( );
if(inputstat.equals("attack")){
heroattack = heroattack + 1;
}
else if(inputstat.equals("speed")){
herospeed = herospeed + 1;
}
else if(inputstat.equals("armorpen")){
heroarmorpen = heroarmorpen + 1;
}
else if(inputstat.equals("armor")){
heroarmor = heroarmor + 1;
}
else if(inputstat.equals("health")){
herohealth = herohealth + 5;
}
else{
System.out.println("Please pick one of the stats");
x = x-1;
}
}
}
}
public class StartGame {
StartGame(){ //runs through output for a new game
System.out.println("Welcome to the game");
System.out.println("Pick your hero's starting stats");
Hero thishero = new Hero(10); //spends your points
System.out.println("Let's Begin!/n/n");
new NewEncounter(); //goes into the encounter loops
}
}
at this point I I try to program an encounter in the NewEncounter class, but that isn't possible because I can't use that instance of my hero that I created. and I am pretty sure that my design is bad in the first place, I think I have read that you should be trying to use static variables as little as possible in the first place. I am still reading trying to grasp this but I have read a lot and I feel like nothing is helping me. I actually think a more "hands on" large scale project/tutorial might help me, but I don't know one. Anyways thanks for any help and sorry for the long/blog-like post.
The standard way to use an instance of one class within another is to either store a reference to it or to pass it into the method that stores or uses it.
For example:
class Encounter {
private final Hero mainHero;
private final List<Hero> participants;
public Encounter(Hero mainHero) {
this.mainHero = mainHero;
participants = new ArrayList<>();
}
public void addParticipant(Hero hero) {
participants.add(hero);
}
}
Allows:
Encounter encounter = new Encounter(hero);
or:
encounter.addParticipant(hero);
These can then be used within the class. For example:
class Encounter {
public void moveAllParticipants() {
participants.forEach(Hero::move);
}
}
I notice you have used a lot of public static class variables. This is unusual and there are few good reasons to use them. One is to define constants. If that's your intent then I suggest you use the following standard form:
private static final int HERO_ATTACK = 1;
If you intend for these to be instance variables (i.e. different for each hero) then they should not be static nor public.
This is the sort of area that is well covered in the various Java tutorials. I suggest working through one of them and coming back here if you have further issues.
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.