5.5 遍历行为

5.5 遍历行为

当需要无数次地遍历一个对象集合时,遍历行为就很有帮助。JSTL提供了forEachforTokens两个执行遍历行为的标签:这两个标签将在接下来的小节中讨论。

5.5.1 forEach标签

forEach标签会无数次地反复遍历body content或者对象集合。可以被遍历的对象包括java.util.Collectionjava.util.Map的所有实现类,以及对象数组或者主类型。也可以遍历java.util.Iteratorjava.util.Enumeration,但不应该在多个行为中使用Iterator或者Enumeration,因为无法重置Iterator或者Enumeration

形式1 重复执行标签体

forEach标签的语法有两种形式。第一种形式是固定次数地重复body content

1
2
3
4
<c:forEach [var="varName"] begin="begin" 
end="end" step="step">
body content
</c:forEach>

形式2 遍历集合

第二种形式用于遍历对象集合:

1
2
3
4
5
<c:forEach items="collection" [var="varName"] 
[varStatus="varStatusName"] [begin="begin"]
[end="end"] [step="step"]>
body content
</c:forEach>

forEach标签的标签体body content之中的代码时JSP代码.

forEach标签的属性

属性 类型 描述
var 字符串 引用遍历的当前项目的有界变量名称
items+ 支持的任意类型 遍历的对象集合
varStatus 字符串 保存遍历状态的有界变量名称。类型值为javax.servlet.jsp.jstl.core.LoopTagStatus
begin+ 整数 如果指定items,遍历将从指定索引处的项目开始,例如,集合中第一个项目的索引为0。如果没有指定items,遍历将从设定的索引值开始。如果指定,begin的值必须大于或者等于0
end+ 整数 如果指定items,遍历将在(含)指定索引处的项目结束。如果没有指定items,遍历将在索引到达指定值时结束
step+ 整数 遍历将只处理间隔指定step的项目,从第一个项目开始。在这种情况下,step的值必须大于或者等于1
对于每一次遍历,forEach标签都将创建一个有界变量,变量名称通过var属性定义。这个有界变量只存在于开始和关闭的forEach标签之间,一到关闭的forEach标签前,它就会被删除。

实例

begin end属性

例如,下列的forEach标签将显示“1 2 3 4 5 ”。

1
2
3
<c:forEach var="x" begin="1" end="5">
<c:out value="${x}" />&nbsp;
</c:forEach>

step属性

1
2
3
<c:forEach var="x" begin="1" end="10" step="2">
<c:out value="${x}" />&nbsp;
</c:forEach>

运行结果:1 3 5 7 9

遍历有界变量

下面的forEach标签将遍历有界变量addressphones属性:

1
2
3
<c:forEach var="phone" items="${address.phones}">
${phone}"<br/>
</c:forEach>

在本例中,有界变量命名为phoneforEach标签中的EL表达式用于显示phone的值。

获取遍历的次数

forEach标签有一个类型为javax.servlet.jsp.jstl.core.LoopTagStatus的变量varStatusLoopTagStatus接口带有count属性,它当前遍历的“次数”。第一次遍历时,status.count值1;第二次遍历时,status.count值为2,依次类推。测试status.count%2的余数,可以知道该标签当前遍历到的是偶数编号的元素,还是奇数编号的元素。

forEach标签遍历ArrayList

app05a应用程序中的BookController类和BookList.jsp页面为例。BookController类调用了一个service方法,返回一个Book对象List

Book类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package app05a.model;
public class Book {
//书号
private String isbn;
//书名
private String title;
//价钱
private double price;
public Book(String isbn, String title, double price) {
this.isbn = isbn;
this.title = title;
this.price = price;
}
public String getIsbn() {
return isbn;
}
public void setIsbn(String isbn) {
this.isbn = isbn;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}

BooksServlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package app05a.servlet;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import app05a.model.Book;
@WebServlet(urlPatterns = {"/books"})
public class BooksServlet extends HttpServlet {
private static final int serialVersionUID = -234237;
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException,
IOException {
//书籍列表
List<Book> books = new ArrayList<Book>();
Book book1 = new Book("978-0980839616",
"Java 7: A Beginner's Tutorial", 45.00);
Book book2 = new Book("978-0980331608",
"Struts 2 Design and Programming: A Tutorial",
49.95);
Book book3 = new Book("978-0975212820",
"Dimensional Data Warehousing with MySQL: A "
+ "Tutorial", 39.95);
//添加几本书到书架上
books.add(book1);
books.add(book2);
books.add(book3);
//在请求对象中设置books属性,保存书籍列表
request.setAttribute("books", books);
RequestDispatcher rd =
request.getRequestDispatcher("/books.jsp");
//请求转发
rd.forward(request, response);
}
}

BooksServletdoGet方法中创建了几本书,保存在书籍列表中,然后把书籍列表设置为请求对象的属性.最后再调用book.jsp进行显示.
books.jsp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<%@page contentType="text/html; charset=utf-8" 
pageEncoding="utf-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
<title>Book List</title>
<style>
table, tr, td {
/* 边框粗1px,实线,边框颜色为 #546e7a */
border: 1px solid #546e7a;
}
</style>
</head>
<body>
Books in Simple Table
<table>
<tr>
<td>ISBN</td>
<td>Title</td>
</tr>
<!-- 遍历请求对象中的books属性所指向的这个ArrayList对象
遍历变量名为book-->
<c:forEach items="${requestScope.books}" var="book">
<tr>
<td>${book.isbn}</td>
<td>${book.title}</td>
</tr>
</c:forEach>
</table>
<br /> Books in Styled Table
<table>
<tr style="background: #0288d1">
<td>ISBN</td>
<td>Title</td>
</tr>
<c:forEach items="${requestScope.books}" var="book"
varStatus="status">
<c:if test="${status.count%2 == 0}">
<tr style="background: #ba68c8">
</c:if>
<c:if test="${status.count%2 != 0}">
<tr style="background: #f06292">
</c:if>
<td>${book.isbn}</td>
<td>${book.title}</td>
</tr>
</c:forEach>
</table>
<br /> ISBNs only:
<c:forEach items="${requestScope.books}" var="book"
varStatus="status">
${book.isbn}<c:if test="${!status.last}">,</c:if>
</c:forEach>
</body>
</html>

其中下面的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<table>
<tr>
<td>ISBN</td>
<td>Title</td>
</tr>
<!-- 遍历请求对象中的books属性所指向的这个ArrayList对象
遍历变量名为book,表示Arraylist中的一个元素,
也就是一个Book对象-->
<c:forEach items="${requestScope.books}" var="book">
<tr>
<!-- 输出book对象isbn编号 -->
<td>${book.isbn}</td>
<!-- 输出book对象的书名 -->
<td>${book.title}</td>
</tr>
</c:forEach>
</table>

使用了forEach标签来遍历保存在请求对象中的books属性(ArrayList),显示效果如下图所示:
这里有一张图片

测试forEach的状态对象

varStatus="status"将创建一个名为status的状态数据对象,使用状态数据对象可以获取本次遍历的一些信息.如status.count表示当前遍历的次数.
测试状态对象的JSP代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<table>
<tr style="background: #0288d1">
<td>ISBN</td>
<td>Title</td>
</tr>
<!-- 遍历请求对象中的books属性(ArrayList),
遍历变量为book(表示ArrayList中的一个元素),
并创建名称为status的遍历状态对象.
-->
<c:forEach items="${requestScope.books}" var="book"
varStatus="status">
<!-- 如果循环是偶数的话,使用xxxxx作为该行的背景颜色 -->
<c:if test="${status.count%2 == 0}">
<tr style="background: #ba68c8">
</c:if>
<!-- 如果循环次数是奇数的话,使用yyyyy作为该行的背景颜色 -->
<c:if test="${status.count%2 != 0}">
<tr style="background: #f06292">
</c:if>
<td>${book.isbn}</td>
<td>${book.title}</td>
</tr>
</c:forEach>
</table>

显示效果:
这里有一张图片

最后一个元素

status.last表示最后的最后一个元素,测试代码如下所示:

1
2
3
4
5
6
7
<br /> ISBNs only:
<c:forEach items="${requestScope.books}" var="book"
varStatus="status">
${book.isbn}
<!-- 如果不是最后一个元素则输出逗号作为分隔符 -->
<c:if test="${!status.last}">,</c:if>
</c:forEach>

forEach标签遍历Map

利用forEach还可以遍历Map。要分别利用keyvalue属性引用一个Map key和一个Map值。遍历Map的伪代码如下:

1
2
3
<c:forEach var="mapItem" items="map">
${mapItem.key} : ${mapItem.value}
</c:forEach>

下一个范例展示了forEachMap的结合使用。CityController类将两个Map实例化,并为它们赋予键/值对。第一个Map中的每一个元素都是一个String/String对,第二个Map中的每一个元素则都是一个String/String[]对。

BigCitiesServlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package app05a.servlet;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(
urlPatterns =
{"/bigCities"}
)
public class BigCitiesServlet extends HttpServlet
{
/**
*
*/
private static final long serialVersionUID = -83191871L;
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException,IOException
{
//创建一个Map
Map<String,String> capitals =
new HashMap<String,String>();
capitals.put("Indonesia", "Jakarta");
capitals.put("Malaysia", "Kuala Lumpur");
capitals.put("Thailand", "Bangkok");
//设置为请求对象的属性,并命名为
request.setAttribute("capitals", capitals);
//创建一个Map
Map<String,String[]> bigCities =
new HashMap<String,String[]>();
bigCities.put("Australia",
new String[]{"Sydney","Melbourne","Perth"});
bigCities.put("New Zealand",
new String[]{"Auckland","Christchurch","Wellington"});
bigCities.put("Indonesia",
new String[]{"Jakarta","Surabaya","Medan"});
request.setAttribute("capitals", capitals);
request.setAttribute("bigCities", bigCities);
//请求转发使用JSP显示
RequestDispatcher rd =
request.getRequestDispatcher("/bigCities.jsp");
rd.forward(request, response);
}
}

bigCities.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<%@page contentType="text/html; charset=UTF-8"
pageEncoding="utf-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core"
prefix="c"%>
<html>
<head>
<title>Big Cities</title>
<style>
table, tr, td {
border: 1px solid #aaee77;
padding: 3px;
}
</style>
</head>
<body>
Capitals
<table>
<tr style="background: #448755; color: white;
font-weight: bold">
<td>Country</td>
<td>Capital</td>
</tr>
<!-- 遍历请求对象中的 capitals属性 -->
<c:forEach items="${requestScope.capitals}"
var="mapItem">
<tr>
<!-- 取出map的key -->
<td>${mapItem.key}</td>
<!-- 取出对应的value -->
<td>${mapItem.value}</td>
</tr>
</c:forEach>
</table>
<br /> Big Cities
<table>
<tr style="background: #448755; color: white;
font-weight: bold">
<td>Country</td>
<td>Cities</td>
</tr>
<!-- 遍历请求对象中的bigCities这个属性(Map)-->
<c:forEach items="${requestScope.bigCities}"
var="mapItem">
<tr>
<td>${mapItem.key}</td>
<!-- 遍历Map的值,也就是String[]数组 -->
<td><c:forEach items="${mapItem.value}"
var="city" varStatus="status">
<!-- 输出逗号作为分隔符,最后元素不输出逗号 -->
${city}<c:if test="${!status.last}">,</c:if>
</c:forEach></td>
</tr>
</c:forEach>
</table>
</body>
</html>

遍历String/String 类型的Map

设置capitals属性

Servlet中设的的capitals请求对象的属性如下所示:

1
2
3
4
5
6
7
8
//创建一个Map
Map<String,String> capitals =
new HashMap<String,String>();
capitals.put("Indonesia", "Jakarta");
capitals.put("Malaysia", "Kuala Lumpur");
capitals.put("Thailand", "Bangkok");
//设置为请求对象的属性,并命名为
request.setAttribute("capitals", capitals);

然后在JSP页面中通过forEach标签遍历该请求对象中的属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Capitals
<table>
<tr style="background: #448755; color: white;
font-weight: bold">
<td>Country</td>
<td>Capital</td>
</tr>
<!-- 遍历请求对象中的 capitals属性 -->
<c:forEach items="${requestScope.capitals}"
var="mapItem">
<tr>
<!-- 取出map的key -->
<td>${mapItem.key}</td>
<!-- 取出对应的value -->
<td>${mapItem.value}</td>
</tr>
</c:forEach>
</table>

显示效果:
这里有一张图片

遍历String/String[]类型的Map

设置请求对象的bigCities属性

1
2
3
4
5
6
7
8
9
10
//创建一个Map
Map<String,String[]> bigCities =
new HashMap<String,String[]>();
bigCities.put("Australia",
new String[]{"Sydney","Melbourne","Perth"});
bigCities.put("New Zealand",
new String[]{"Auckland","Christchurch","Wellington"});
bigCities.put("Indonesia",
new String[]{"Jakarta","Surabaya","Medan"});
request.setAttribute("bigCities", bigCities);

遍历请求对象的bigCities属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<br /> Big Cities
<table>
<tr style="background: #448755;
color: white; font-weight: bold">
<td>Country</td>
<td>Cities</td>
</tr>
<!-- 遍历请求对象中的bigCities这个属性(Map)-->
<c:forEach items="${requestScope.bigCities}"
var="mapItem">
<tr>
<td>${mapItem.key}</td>
<!-- 遍历Map的值,也就是String[]数组 -->
<td><c:forEach items="${mapItem.value}"
var="city" varStatus="status">
<!-- 输出逗号作为分隔符,最后元素不输出逗号 -->
${city}<c:if test="${!status.last}">,</c:if>
</c:forEach></td>
</tr>
</c:forEach>
</table>

显示效果如下:
这里有一张图片