Generate Data Points for Graph from an equation
<p><strong>I don't want to solve an equation and my question is not about Graphs and Trees Data Structures</strong>. I am trying to generate Data Points for graph from an equation given by user. I want efficient algorithm, easy to use and easy to maintain data structures. I have two solutions in mind</p>

<p>1: This is trivial and I have seen in many Applications.</p>

<pre><code>String expr = "2*x+3*x";
Evaluator evaluator = new Evaluator();//I have this class

for (int i = start; i &lt; end; i += step)
{
    evaluator.setConstant("x", i); 
    double ans = evaluator.evaluate(expr);
}
</code></pre>

<p>This is very slow because each time every step is repeated like tokenzing, verifying, conversion to RPN, preparing stacks and queues and at last result calculation. The possible solution to this problem is somehow caching all stacks and queues but after that a comparison would be required between current expression and previous expression to use last stored state.</p>

<p>2: Currently I am developing second solution. The purpose of this is efficiency and would be used in Symbolic calculation in future.</p>

<p>So far my implementation</p>

<p>Variable.java</p>

<pre><code>import java.text.DecimalFormat;

public class Variable
{
    private final double pow;
    private final double coefficient;
    private final String symbol;

    public Variable(String symbol)
    {
        this.symbol = symbol;
        this.pow = 1.0;
        this.coefficient = 1.0;
    }

    public Variable(String symbol, double coefficient, double pow)throws IllegalArgumentException
    {
        if (coefficient == 0.0)throw new IllegalArgumentException("trying to create variable with coefficient 0");
        if (pow == 0.0)throw new IllegalArgumentException("trying to create variable with exponent 0");

        this.symbol = symbol;
        this.pow = pow;
        this.coefficient = coefficient;
    }

    public final String getSymbol()
    {
        return this.symbol;
    }

    public final double getPow()
    {
        return this.pow;
    }

    public final double getCoefficient()
    {
        return this.coefficient;
    }

    @Override
    public String toString()
    {
        StringBuilder builder = new StringBuilder();
        DecimalFormat decimalFormat = new DecimalFormat("#.############");
        if (coefficient != 1.0)builder.append(decimalFormat.format(this.coefficient));
        builder.append(this.symbol);
        if (this.pow != 1.0)builder.append("^").append(decimalFormat.format(this.pow));

        return builder.toString();
    }

    /*
    * Stub Method
    * Generate some unique hash code
    * such that chances of key collision
    * become less and easy to identify 
    * variables with same power and same
    * symbol*/
    @Override
    public int hashCode()
    {
        return 0;
    }
}
</code></pre>

<p>Equation.java</p>

<pre><code>import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

public class Equation
{
    private final ArrayList&lt;Boolean&gt; operations;
    private final HashMap&lt;String, Variable&gt; variableHashMap;
    private int typesOfVariables;

    public Equation(Variable variable)
    {
        this.variableHashMap = new HashMap&lt;&gt;();
        this.operations = new ArrayList&lt;&gt;();
        this.typesOfVariables = 1;

        this.variableHashMap.put(variable.getSymbol(), variable);
    }

    /*Stub Method*/
    public void addVariable(Variable variable, boolean multiply)
    {
        /*
         * Currently not covering many cases
         * 1: Add two variables which have same name
         * and same pow.
         * 2: variable which are wrapped inside functions e.g sin(x)
         * and many other.*/
        if (multiply &amp;&amp; variableHashMap.containsKey(variable.getSymbol()))
        {
            Variable var = variableHashMap.get(variable.getSymbol());
            Variable newVar = new Variable(var.getSymbol(), var.getCoefficient() * variable.getCoefficient(), var.getPow() + variable.getPow());
            /*
             * Collision chances for variables with same name but
             * with different powers*/
            this.variableHashMap.replace(var.getSymbol(), newVar);
        }
        else
        {
            ++this.typesOfVariables;
            this.variableHashMap.put(variable.getSymbol(), variable);
        }
        this.operations.add(multiply);
    }

    /*Stub Method
     *Value for every variable at any point will be different*/
    public double solveFor(double x)
    {
        if (typesOfVariables &gt; 1)throw new IllegalArgumentException("provide values for all variables");

        Iterator&lt;HashMap.Entry&lt;String, Variable&gt;&gt; entryIterator = this.variableHashMap.entrySet().iterator();

        Variable var;
        double ans = 0.0;
        if (entryIterator.hasNext())
        {
            var = entryIterator.next().getValue();
            ans = var.getCoefficient() * Math.pow(x, var.getPow());
        }

        for (int i = 0; entryIterator.hasNext(); i++)
        {
            var = entryIterator.next().getValue();
            if (this.operations.get(i))ans *= var.getCoefficient() * Math.pow(x, var.getPow());
            else ans += var.getCoefficient() * Math.pow(x, var.getPow());
        }
        return ans;
    }

    @Override
    public String toString()
    {
        StringBuilder builder = new StringBuilder();
        Iterator&lt;HashMap.Entry&lt;String, Variable&gt;&gt; entryIterator = this.variableHashMap.entrySet().iterator();

        if (entryIterator.hasNext())builder.append(entryIterator.next().getValue().toString());

        Variable var;
        for (int i = 0; entryIterator.hasNext(); i++)
        {
            var = entryIterator.next().getValue();
            if (this.operations.get(i))builder.append("*").append(var.toString());
            else builder.append(var.toString());
        }

        return builder.toString();
    }
}
</code></pre>

<p>Main.java</p>

<pre><code>class Main
{
    public static void main(String[] args)
    {
        try
        {
            long t1 = System.nanoTime();

            Variable variable = new Variable("x");
            Variable variable1 = new Variable("x", -2.0, 1.0);
            Variable variable2 = new Variable("x", 3.0, 4.0);

            Equation equation = new Equation(variable);
            equation.addVariable(variable1, true);//2x+x
            equation.addVariable(variable2, true);

            for (int i = 0; i &lt; 1000000; i++)equation.solveFor(i);//Calculate Million Data Points
            long t2 = System.nanoTime();

            System.out.println((t2-t1)/1000/1000);
            System.out.println(equation.toString());
        }
        catch (Exception e)
        {
            System.out.println("Error: " + e.getMessage());
        }
    }
}
</code></pre>

<p>Am I going in right direction? 
Is there any commonly used Algorithm for this problem?</p>

<p>My main goal is efficiency, code cleanness and code maintainability.</p>

<p>Note: I am not native English speaker so please ignore any grammatical mistake.</p>

<p>Thanks.</p>