在c:forEach循环中计算所有数字的总和

21

我有一个 Java bean,代码如下:

class Person {
  int age;
  String name;
}

我想在JSP中遍历这些bean的集合,将每个人显示为HTML表格行,在表格的最后一行中,我想显示所有年龄的总和。

生成表格行的代码将类似于以下内容:

<c:forEach var="person" items="${personList}">
  <tr><td>${person.name}<td><td>${person.age}</td></tr>
</c:forEach>

然而,我正在努力寻找一种计算年龄总和的方法,该方法将显示在最后一行而不需使用脚本代码。有什么建议吗?


你是在将所有年龄相加并在最后一行显示吗? - ScArcher2
7个回答

44
注意:我试图将答案结合起来,以制作全面的列表。我在适当的情况下提到了名称,以表彰功劳。
有许多解决此问题的方法,每种方法都有其优缺点:
纯JSP解决方案
正如ScArcher2上面提到的那样,一个非常简单易行的解决方案是在JSP中直接实现它。
<c:set var="ageTotal" value="${0}" />
<c:forEach var="person" items="${personList}">
  <c:set var="ageTotal" value="${ageTotal + person.age}" />
  <tr><td>${person.name}<td><td>${person.age}</td></tr>
</c:forEach>
${ageTotal}

这个解决方案的问题在于 JSP 变得混乱,甚至可能引入脚本。如果您预期每个人都能够遵循基本逻辑,那么这是一个很好的选择。 纯 EL 解决方案 如果您已经使用 EL 3.0(Java EE 7 / Servlet 3.1),可以使用新的 streams 和 lambdas 支持:
<c:forEach var="person" items="${personList}">
  <tr><td>${person.name}<td><td>${person.age}</td></tr>
</c:forEach>
${personList.stream().map(person -> person.age).sum()}

JSP EL函数

另一种在JSP中输出总数的方法是使用EL函数。EL函数允许您调用公共类中的公共静态方法。例如,如果您想要遍历集合并求和值,可以在名为PersonUtils的公共类中定义一个名为sum(List people)的公共静态方法。在tld文件中,您需要放置以下声明:

<function>
  <name>sum</name>
  <function-class>com.example.PersonUtils</function-class>
  <function-signature>int sum(java.util.List people)</function-signature>
</function> 

在你的JSP中,你需要编写如下代码:

<%@ taglib prefix="f" uri="/your-tld-uri"%>
...
<c:out value="${f:sum(personList)}"/>

JSP EL函数具有一些好处。它们允许您使用现有的Java方法,而无需编写特定于UI(自定义标记库)的代码。它们也很紧凑,并不会使非编程导向的人困惑。
另一个选择是创建自己的自定义标记。这个选项将涉及最多的设置,但会给你我认为你最终要寻找的东西,绝对没有脚本片段。可以在http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/JSPTags5.html#74701中找到使用简单自定义标记的很好的教程。
所涉步骤包括子类化TagSupport:
public PersonSumTag extends TagSupport {

   private List personList;

   public List getPersonList(){
      return personList;
   }

   public void setPersonList(List personList){
      this.personList = personList;
   }

   public int doStartTag() throws JspException {
      try {
        int sum = 0;
        for(Iterator it = personList.iterator(); it.hasNext()){
          Person p = (Person)it.next();
          sum+=p.getAge();
        } 
        pageContext.getOut().print(""+sum);
      } catch (Exception ex) {
         throw new JspTagException("SimpleTag: " + 
            ex.getMessage());
      }
      return SKIP_BODY;
   }
   public int doEndTag() {
      return EVAL_PAGE;
   }
}

在 tld 文件中定义标签:

<tag>
   <name>personSum</name>
   <tag-class>example.PersonSumTag</tag-class>
   <body-content>empty</body-content>
   ...
   <attribute>
      <name>personList</name>
      <required>true</required>
      <rtexprvalue>true</rtexprvalue>
      <type>java.util.List</type>
   </attribute>
   ...
</tag>

在JSP页面的顶部声明标签库:

<%@ taglib uri="/you-taglib-uri" prefix="p" %>

并使用标签:

<c:forEach var="person" items="${personList}">
  <tr><td>${person.name}<td><td>${person.age}</td></tr>
</c:forEach>
<p:personSum personList="${personList}"/>

显示标签

正如zmf之前提到的,您也可以使用显示标签,但是您需要包含适当的库:

http://displaytag.sourceforge.net/11/tut_basic.html


在控制器中计算总数 -- 而不是在 JSP 中 -- 是非常可取的。使用 MVC 框架,例如 Spring,而不是试图在 JSP 或 JSTL 中做太多事情。 - Thomas W

5

你是在尝试计算所有年龄吗?

你可以在控制器中计算,然后只在jsp中显示结果。

你可以编写自定义标签来进行计算。

你可以使用jstl在jsp中像这样计算。

<c:set var="ageTotal" value="${0}" />
<c:forEach var="person" items="${personList}">
  <c:set var="ageTotal" value="${ageTotal + person.age}" />
  <tr><td>${person.name}<td><td>${person.age}</td></tr>
</c:forEach>
${ageTotal}


1

ScArcher2有最简单的解决方案。如果你想要尽可能紧凑的东西,你可以创建一个带有“sum”函数的标签库。像这样:

class MySum {
public double sum(List list) {...}
}

在你的顶级域名中:
<function>
<name>sum</name>
<function-class>my.MySum</function-class>
<function-signature>double sum(List)</function-signature>
</function>

在你的JSP中,你会有类似这样的东西:
<%@ taglib uri="/myfunc" prefix="f" %>

${f:sum(personList)}

不够灵活--无法指定属性或函数结果sum()的计算目的。要么您已经走了一条冗长的路来制作单一用途的运算符,要么您必须进一步使用反射技术来使其更加灵活。 - Thomas W
简短而正确的回答:在控制器中计算摘要,而不是在JSP中。 - Thomas W

0

您可以按照以下方式使用JSTL迭代集合

<c:forEach items="${numList}" var="item">
      ${item}
</c:forEach>

如果它是一张地图,你可以执行以下操作。
<c:forEach items="${numMap}" var="entry">
  ${entry.key},${entry.value}<br/>
</c:forEach>

0

这个方法有点巧妙,但在您的控制器代码中,您可以创建一个带有总数的虚拟 Person 对象!

您是如何检索对象的?HTML 允许您设置一个 <TFOOT> 元素,它将位于您拥有的任何数据底部,因此您可以将总数单独设置为 Person 对象,并将其原样输出到页面上,而无需在 JSP 页面上进行任何计算。


0

在控制器中计算总数或其他摘要 – 而不是在JSP中 – 是非常可取的。

使用Java代码和MVC方法,例如Spring MVC框架,而不是试图在JSP或JSTL中做太多的事情;在这些语言中进行重要计算会使您的JSP页面变得弱且缓慢,并使其变得不那么清晰。

例:

class PersonList_Controller implements Controller {
    ...
    protected void renderModel (List<Person> items, Map model) {
        int totalAge = 0;
        for (Person person : items) {
            totalAge += person.getAge();
        }
        model.put("items", items);
        model.put("totalAge", totalAge);
    }
}

从设计决策的角度来看,任何需要总数的地方,可能在下个月扩展为需要平均数、中位数、标准差。

JSTL计算和汇总仅仅能够得到一个总数。你真的想在JSTL中满足更多的汇总要求吗?我认为答案是否定的,因此正确的设计决策是在控制器中进行计算,这同样/更加简单,并且明显更具可扩展性。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接