I'm currently implementing an algorithm that constructs a matrix-pattern based on mathematical formulas. To achieve this I use deeply nested for-loops and alot of if-conditions in it. The problem is, that I cannot split the loop into multiple method without providing a lot of parameters. And for now the code looks like an undesired spaghetti-code.
Here is a small pseudo-example:
int steps = 10;
void evaluate( int numOuterArea , int numInnerArea , int[] solution , int[] factor , int[] indices )
{
int counterA = 0;
int counterB = 0;
for( int outerAreaIter = 0 ; outerAreaIter < numOuterArea ; outerAreaIter++ )
{
for( int curOuterAreaIter = 0 ; curOuterAreaIter < steps ; curOuterAreaIter++ )
{
for( int innerAreaIter = 0 ; innerAreaIter < numInnerArea ; innerAreaIter++ )
{
for( int curInnerAreaIter = 0 ; curInnerAreaIter < curOuterAreaIter ; curInnerAreaIter++ )
{
if( curInnerAreaIter == curOuterAreaIter )
{
// do something with solution, factor or indices
}
else if( /* some other fancy condition */)
{
}
...
}
}
}
}
// similar nested loops follow here
}
If I would write classes/methods for each loop or part of a loop, I have to provide all parameters from evaluate() (which can be even more as shown in the example) and also all previous iterators and possible variables.
Is there a way/common practice/any hints or advice to rewrite such code in a better way?
The simplest way is encapsulation of all parameters in the single object. You can use this object to passing data as sole parameter into evaluation method. Something like this example:
class EvaluationContext {
int numOuterArea;
int numInnerArea;
int[] solution;
int[] factor;
int[] indices;
}
interface Evaluator {
void evaluate(EvaluationContext ctx);
}
class FirstEvaluator implements Evaluator {
void evaluate(EvaluationContext ctx) {
SecondEvaluator e2 = new SecondEvaluator();
for (...) {
e2.evaluate(ctx);
}
}
}
class SecondEvaluator implements Evaluator {
void evaluate(EvaluationContext ctx) {
// evaluate something and put result into context
}
}
A simple design pattern is a Method Object. Simply write a class which is responsible for this calculation. You then can have fields that simply store intermediate results during that calculation. With this approach, you do not need to pass any arguments.
Example:
class EvaluateMethod {
private final int numOuterArea;
private final int numInnerArea;
private final int[] solution;
private final int[] factor;
private final int[] indices;
// place fields for intermediate results here
EvaluateMethod(int numOuterArea, int numInnerArea, int[] solution, int[] factor, int[] indices) {
// assign all parameter to fields here
}
void execute() {
// Your method body as before comes here.
// But you can extract methods easily.
}
}
One additional note: You cannot reuse an instance of this class. I call them one-shot-objects that must be instantiated, used, and discarded.
Related
Question : Can i realize method private void fillingArrayList() use Java Stream API (that is in one line) . The variable i is needed to define a length of String ;
I try a for each loop but it doesn't work . I need a range for loop.
import org.apache.commons.lang3.StringUtils;
public class Tolls {
public static String digitsConcatenation(short number, long times) {
return StringUtils.repeat(Character.forDigit(number, 10), Long.valueOf(times).intValue());
}
}
public class Progression {
public Progression(Digit digit) {
this.digit = digit;
this.numbers = new ArrayList<>(Long.valueOf(digit.getTimes()).intValue());
this.fillingArrayList();
}
public Optional<Long> getProgressionSum() {
return this.numbers.stream().reduce(Long::sum);
}
public List<Long> getNumbers() {
return Collections.unmodifiableList(this.numbers);
}
private void fillingArrayList() {
for (int i = 1; i <= digit.getTimes(); i++)
this.numbers.add(Long.valueOf(Tolls.digitsConcatenation(digit.getNumber(), i)));
}
private final List<Long> numbers;
private final Digit digit;
}
My Try :
private void fillingArrayList() {
Arrays.stream(this.numbers.toArray())
.forEach(i-> this.numbers.add(
Long.valueOf(Tolls.digitsConcatenation(
digit.getNumber(), (Long) i))));
}
There are some weird things in your code, like in Progression’s constructor, writing an expression like Long.valueOf(digit.getTimes()).intValue() instead of just digit.getTimes(). The constructor of ArrayList expects an int and digit.getTimes() returns an int (or a type implicitly convertible to int), as demonstrated with the loop condition i <= digit.getTimes().
Likewise, the expression Long.valueOf(times).intValue() within the digitsConcatenation method, which is a cast from long to int in disguise, is only necessary because you declared the second parameter of digitsConcatenation as long despite you actually need an int and the caller’s argument is an int, so you could declare it as int in the first place.
But the entire approach of using string concatenation (incorporating a 3rd party library) and parsing it back into a number is unnecessarily complicated and inefficient. Since both conversions are implicitly using the decimal system, the operation’s result is the same as multiplying the number with ten and adding the value of the digit.
So you could just use
private void fillingArrayList() {
int n = digit.getNumber();
LongStream.iterate(n, current -> current * 10 + n)
.limit(digit.getTimes()).forEach(numbers::add);
}
without any string operation.
Even better would be to change the constructor to
public Progression(Digit digit) {
this.digit = digit;
int n = digit.getNumber();
this.numbers = LongStream.iterate(n, current -> current * 10 + n)
.limit(digit.getTimes()).boxed()
.collect(Collectors.toList());
}
letting the stream produce the List<Long> instead of constructing it manually and modify it after construction.
Following should work:
IntStream.rangeClosed(1, digit.getTimes()).forEach(i -> this.numbers.add(Long.valueOf(Tolls.digitsConcatenation(digit.getNumber(), i))););
This is the signature of my class:
public class Constraint extends ArrayList<Interval> {
// ...
}
the other class Interval:
public class Interval {
// ...
}
has two ints, first and last
Constructor:
public Interval(int first, int last) {
this.first = first;
this.last = last;
}
A method that returns the union of two Intervals or more but should be of Constraint type:
public Constraint union(Interval interval) {
Interval a = new Interval(first, end);
int l = 0;
int max = 0;
// Interval result = new Interval(l, max);
l = (a.first < interval.end) ? a.first : interval.end;
max = (a.end > interval.end) ? a.end : interval.end;
return new Interval(l, max);
// the return here will return a new interval of type Interval but
// the method that I'm suppose to write should return something of
// type Constraint
}
My main issue is: how can I write the following constructor?
public Constraint(Collection<Interval> collection) throws NullPointerException {
// if the collection is empty, I have to write something like this:
if (collection == null) {
throw new NullPointerException("collection is empty");
}
// ...
}
Any help on how I should write the constructor of the class Constraint is really appreciated.
You have stumbled upon a deficiency of Java: the call to super() must be the first call in your constructor, so you are not allowed to precede it with code that checks against null. There is no good reason for this limitation, it is just the way java is. A flaw in the language. (If you learn Scala you will see that it totally did not have to be this way.)
So, the solution is to do the null-check in the same statement as the call to super. The following should accomplish this:
public Constraint( Collection<Interval> collection )
{
super( Objects.requireNonNull( collection ) );
}
If your version of java does not have Objects.requireNonNull() you can code a private static function yourself which checks the collection for nullity, throws if null, or returns the collection as-is if not null.
I have a Object which contains a list of another object which contains a list of another object and so on... suppose I want to get count of nested list elements(lets say last one), what should be best approach rather than using traditional for loop in java as I have done in below example -
public static void main(String[] args) {
Statement statement = new Statement();
statement.getInvAccount().add(new InvestmentAccount());
statement.getInvAccount().get(0).getSecAccountStmt().add(new SecurityStatement());
statement.getInvAccount().get(0).getSecAccountStmt().get(0).getTransactionStatement().add(new TransactionStatement());
statement.getInvAccount().get(0).getSecAccountStmt().get(0).getTransactionStatement().add(new TransactionStatement());
statement.getInvAccount().get(0).getSecAccountStmt().get(0).getTransactionStatement().add(new TransactionStatement());
// method to count the number of TransactionStatement
System.out.println("Size of TransactionStatement is : " + count(statement));
}
private static int count(Statement stmt) {
int countOfTransStmt = 0;
for (InvestmentAccount invAcc : stmt.getInvAccount()) {
if (invAcc != null) {
for (SecurityStatement secStmt : invAcc.getSecAccountStmt()) {
if (secStmt != null) {
countOfTransStmt = countOfTransStmt + secStmt.getTransactionStatement().size();
}
}
}
}
return countOfTransStmt;
}
In Java 7 you're not going to do better than two for loops. I wouldn't bother with anything different.
In Java 8 you can use streams to flatten it out:
private static int count(Statement stmt) {
return stmt.getInvAccount().stream()
.filter(Objects::nonNull)
.flatMap(InvestmentAccount::getSecAccountStmt)
.filter(Objects::nonNull)
.flatMap(SecurityStatement::getTransactionStatement)
.count();
}
I would encourage you to get rid of the null checks. If you're going to ignore nulls, better to just expect them not to be inserted in the first place. It'll get rid of a lot of extra if checks throughout your code, I expect.
I'd also encourage you not to abbreviate your variables and methods. Spell out "statement" and "investment" and the like. The abbreviations are harder to read and the brevity isn't really a win.
Similarly, try to use more descriptive method names. countTransactions is better for the main method. And for the various getters, methods that return lists ought to be plural: "getAccounts" rather than "getAccount". Notice how the getters now match the class names; if you know the class name, you know the getter name. You don't have to guess if one or the other is abbreviated:
private static int countTransactions(Statement statement) {
return statement.getInvestmentAccounts().stream()
.flatMap(InvestmentAccount::getSecurityStatements)
.flatMap(SecurityStatement::getTransactionStatements)
.count();
}
Recursion could work in this case:
General idea below:
private int countTransactions(object t)
{
int sum = 0;
if (t == null) return 0;
for (int i = 0; i < t.getAllSub().count; i++)
{
sum += countTransactions(t.subAt(i));
}
return sum;
}
The class is called Exposicion and has a String and an INT value, so I used it as an array to grab some input from the user.
class Exposicion {
public String nombreExpo;
public int duracionExpo;
Exposicion(String nombreExpo, int duracionExpo) {
this.nombreExpo = nombreExpo;
this.duracionExpo = duracionExpo;
}
}
With the Function SortExpo I plan to copy only the values of the array as long as the INT values don't add up to 180, but java flags an error when doing:
arrExpoT[posHor].nombreExpo = arrExpoS[k].nombreExpo;
This is the whole function
void SortExpo(Exposicion[] arrExpoS,int posicion,Exposicion[] arrExpoT){
int poshor=0;
int total=0;
for (int k = 0; k < posicion; k++) {
if ( total < 180 || arrExpoS[poshor].nombreExpo != "TOMADO123") {
arrExpoT[poshor].nombreExpo = arrExpoS[k].nombreExpo;
arrExpoT[poshor].duracionExpo = arrExpoS[k].duracionExpo;
arrExpoS[poshor].nombreExpo = "TOMADO123";
total = total + arrExpoS[k].duracionExpo;
poshor++;
} else {
k = posicion;
}
}
}
Error
I've added the .java file in this link
Also Main.java if this helps
You are getting a NullPointerException because "expo1" and "sala1" variables are both null. You have to pass a reference to an object on both variables. Something like this:
class SalaExpo(){
Exposicion[] expo1=new Exposicion[100];
}
public class ConsoleMenu {
private SalaExpo sala1;
void execute(){
sala1 = new SalaExpo();
}
}
Also you should poblate the sala1.expo1 array, like this (don't know if this is what you are intending but you should do this in order not to get a NullPointerException) :
void GuardarExpo(Exposicion[] arrExpoG,int posicion,Exposicion[] arrSala) {
/*
Bunch
of
code
*/
arrExpoG[posicion] = new Exposicion(inputNombre,inputDuracion);
arrSala[posicion]=arrExpoG[posicion];
}
Finally, you should use the variable "posicion" instead of "sala1.expo1.length" to pass as argument to the "imprimirExpo" method, since the array "sala1.expo1" has a length of 100, that means a lot of null elements since you are not poblating it all:
ImprimirExpo(sala1.expo1,posicion);
instead of:
ImprimirExpo(sala1.expo1,sala1.expo1.length);
Ok, here is the code and then the discussion follows:
public class FlatArrayList {
private static ArrayList<TestWrapperObject> probModel = new ArrayList<TestWrapperObject>();
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
int [] currentRow = new int[10];
int counter = 0;
while (true) {
for (int i = 0; i < 10; i++) {
currentRow[i] = probModel.size();
}
TestWrapperObject currentWO = new TestWrapperObject(currentRow);
probModel.add(counter, currentWO);
TestWrapperObject testWO = probModel.get(counter);
// System.out.println(testWO);
counter++;
if (probModel.size() == 10) break;
}
// Output the whole ArrayList
for (TestWrapperObject wo:probModel) {
int [] currentTestRow = wo.getCurrentRow();
}
}
}
public class TestWrapperObject {
private int [] currentRow;
public void setCurrentRow(int [] currentRow) {
this.currentRow = currentRow;
}
public int [] getCurrentRow() {
return this.currentRow;
}
public TestWrapperObject(int [] currentRow) {
this.currentRow = currentRow;
}
}
What is the above code supposed to do? What I am trying to do is load an array as a member of some wrapper object (TestWrapperObject in our case). When I get out of the loop,
the probModel ArrayList has the number of elements it is supposed to have but all have the same value of the last element (an array of size 10 with each item equal to 9). This is not the case inside the loop. If you perform the same "experiment" with a primitive int value everything works fine. Am I missing something myself regarding arrays as object members? Or did I just encounter a Java bug? I am using Java 6.
You are only creating one instance of the currentRow array. Move that inside the row loop and it should behave more like you expect.
Specifically, the assignment in setCurrentRow does not create a copy of the object, but only assigns the reference. So each copy of your wrapper object will hold a reference to the same int[] array. Changing the values in that array will make the values appear to change for all other wrapper objects that hold a reference to the same instance of the array.
i don' t want to sound condescending, but always try to remember tip #26 from the excellent pragmatic programmer book
select isn't broken
it is very rare to find a java bug. keeping this in mind often helps me to look over my code again, turn it around, and shake out the loose bits until i finally discover where i was wrong. of course asking for help early enough is very encouraged, too :)