Skip to main content

Standard Deviation: An Illustration of Expression Language 3.0 in Servlet Environment

Posted by swchan2 on July 1, 2013 at 3:51 PM PDT

Expression Language (EL) was first introduced as part of JSTL 1.0, was then moved JSP 2.0 and was unified with JSF 1.2 in JSP 2.1. In Java EE 7, EL is a new separate JSR, JSR 341. Many new features are introduced in EL 3.0.

This blog shows how to use new following new features of EL 3.0:

  • Standalone environment
  • Lambda expression (section 1.20 of EL 3.0 spec)
  • The new operator ; to separate statements
  • Retrieval of a Stream from a Collection (section 2.3.1 of EL 3.0 spec)
  • The use of a static field and method, for instance, Math.sqrt

This blog also shows how to use new operations to calculate a standard deviation in the following different ways:

  • Using count, sum and map
  • Using forEach
  • Using reduce (see section 2.3.17 of EL 3.0 spec)

I would like to thank Kinman Chung, the EL specification lead, for useful discussion.

The following example shows some of the new EL features.

public class ELTestServlet extends HttpServlet {
    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse res)
            throws ServletException, IOException {
       
        List list = new ArrayList<>();
        list.add(Double.valueOf(1.0));
        list.add(Double.valueOf(2.0));
        list.add(Double.valueOf(3.0));

        ELProcessor elp = new ELProcessor();
        elp.defineBean("data", list);   

        Object message = (Double)elp.eval(
            "n = data.stream().count(); s = data.stream().sum(); sq = data.stream().map(i -> i*i).sum(); Math.sqrt(sq/n - Math.pow(s/n, 2))");
        res.getWriter().println(message);

        message = (Double)elp.eval(
            "n = 0; s = 0; sq = 0; data.stream().forEach(d -> (n = n + 1; s = s + d; sq = sq + d*d)); Math.sqrt(sq/n - Math.pow(s/n, 2))");
        res.getWriter().println(message);

        message = (Double)elp.eval(
            "n = 0; s = 0; sq = data.stream().reduce(0, (a, i) -> (n = n + 1; s = s + i; a + i*i)); Math.sqrt(sq/n - Math.pow(s/n, 2))");
        res.getWriter().println(message);
    }
}

We will use EL in a servlet as a standalone environment. In this case, an ELProcessor is created and beans are defined as follows:

    ELProcessor elp = new ELProcessor();
    elp.defineBean("data", list);

An EL can be evaluated by using elp.eval(...).

EL Example 1: Using count, sum and map

    n = data.stream().count();
    s = data.stream().sum();
    sq = data.stream().map(i -> i*i).sum();
    Math.sqrt(sq/n - Math.pow(s/n, 2))

For a given collection, stream() allows access to the corresponding Stream.

Then count() and sum() can be called on the Stream to count the number of elements and compute the sum of elements in the associated collection.

For data.stream().map(i -> i*i), a Lambda expression is used. In this case, the expression changes each value in the Stream to its square.

EL Example 2: Using forEach

    n = 0; s = 0; sq = 0;
    data.stream().forEach(d -> (n = n + 1; s = s + d; sq = sq + d*d));
    Math.sqrt(sq/n - Math.pow(s/n, 2))

This EL uses forEach for a stream. The count, sum and sum of squares are computed at the same time. Note that the variables n, s and sq are defined in EL Example 1. We need to reset them to initial values in the given ELProcessor.

EL Example 3: Using reduce

    n = 0; s = 0;
    sq = data.stream().reduce(0, (a, i) -> (n = n + 1; s = s + i; a + i*i));
    Math.sqrt(sq/n - Math.pow(s/n, 2))

In this example, reduce is used to compute the count, sum and sum of squares. In the second line, the first argument of reduce is the initial value of the seed variable a. The result of the expression is assigned back to the variable sq for subsequent computation.

We can learn more about EL 3.0 by looking at the specification in JSR 341. EL 3.0 is available at java.net and is part of GlassFish 4.0.