EL access a map value by Integer key
Initial answer (EL 2.1, May 2009)
As mentioned in this java forum thread:
Basically autoboxing puts an Integer object into the Map.
ie:
map.put(new Integer(0), "myValue")
EL (Expressions Languages) evaluates 0 as a Long and thus goes looking for a Long as the key in the map.
ie it evaluates:
map.get(new Long(0))
As a Long
is never equal to an Integer
object, it does not find the entry in the map.
That's it in a nutshell.
Update since May 2009 (EL 2.2)
Dec 2009 saw the introduction of EL 2.2 with JSP 2.2 / Java EE 6, with a few differences compared to EL 2.1.
It seems ("EL Expression parsing integer as long") that:
you can call the method
intValue
on theLong
object self inside EL 2.2:
<c:out value="${map[(1).intValue()]}"/>
That could be a good workaround here (also mentioned below in Tobias Liefke's answer)
Original answer:
EL uses the following wrappers:
Terms Description Type
null null value. -
123 int value. java.lang.Long
123.00 real value. java.lang.Double
"string" ou 'string' string. java.lang.String
true or false boolean. java.lang.Boolean
JSP page demonstrating this:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ page import="java.util.*" %>
<h2> Server Info</h2>
Server info = <%= application.getServerInfo() %> <br>
Servlet engine version = <%= application.getMajorVersion() %>.<%= application.getMinorVersion() %><br>
Java version = <%= System.getProperty("java.vm.version") %><br>
<%
Map map = new LinkedHashMap();
map.put("2", "String(2)");
map.put(new Integer(2), "Integer(2)");
map.put(new Long(2), "Long(2)");
map.put(42, "AutoBoxedNumber");
pageContext.setAttribute("myMap", map);
Integer lifeInteger = new Integer(42);
Long lifeLong = new Long(42);
%>
<h3>Looking up map in JSTL - integer vs long </h3>
This page demonstrates how JSTL maps interact with different types used for keys in a map.
Specifically the issue relates to autoboxing by java using map.put(1, "MyValue") and attempting to display it as ${myMap[1]}
The map "myMap" consists of four entries with different keys: A String, an Integer, a Long and an entry put there by AutoBoxing Java 5 feature.
<table border="1">
<tr><th>Key</th><th>value</th><th>Key Class</th></tr>
<c:forEach var="entry" items="${myMap}" varStatus="status">
<tr>
<td>${entry.key}</td>
<td>${entry.value}</td>
<td>${entry.key.class}</td>
</tr>
</c:forEach>
</table>
<h4> Accessing the map</h4>
Evaluating: ${"${myMap['2']}"} = <c:out value="${myMap['2']}"/><br>
Evaluating: ${"${myMap[2]}"} = <c:out value="${myMap[2]}"/><br>
Evaluating: ${"${myMap[42]}"} = <c:out value="${myMap[42]}"/><br>
<p>
As you can see, the EL Expression for the literal number retrieves the value against the java.lang.Long entry in the map.
Attempting to access the entry created by autoboxing fails because a Long is never equal to an Integer
<p>
lifeInteger = <%= lifeInteger %><br/>
lifeLong = <%= lifeLong %><br/>
lifeInteger.equals(lifeLong) : <%= lifeInteger.equals(lifeLong) %> <br>
JSTL Access Integer/Long key in Hash Map
What is currObj
? Can you redefine its currVal
member as a Long
(or long
)?
A numeric literal (matching the IntegerLiteral
production in the EL syntax) will be represented as a Long
. The expression currObj.currVal
evaluates to an Integer
. A Long
never equals()
an Integer
, so one expression must result in a different type.
Essentially, what you need is an explicit type conversion. Nothing like this is built into EL, but you could create a custom EL function to do it for you. This is a static function that you implement in Java, then describe in a TLD. Another answer of mine gives an example of the packaging. Here's what the function and its usage could look like in your case.
package com.y.taglib.core;
public final class CoercionUtil {
public static Long toLong(Long n) {
return n;
}
}
The TLD would look like this:
<taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0">
<tlib-version>1.0</tlib-version>
<short-name>x-c</short-name>
<uri>http://dev.y.com/taglib/core/1.0</uri>
<function>
<description>Coerce to a java.lang.Long.</description>
<display-name>long</display-name>
<name>long</name>
<function-class>com.y.taglib.core.CoercionUtil</function-class>
<function-signature>java.lang.Long toLong(java.lang.Long)</function-signature>
</function>
</taglib>
In your JSP:
<%@taglib uri="http://dev.y.com/taglib/core/1.0" prefix="my" %>
...
<c:if test='${ ! empty testMap[my:long(currObj.currVal)]}'>
The JSP engine takes care of the necessary type coercion (from the Integer
result of currVal
to the Long
required by the toLong()
method. Your method is there simply to indicate the required type; without it, the JSP engine sees the (erased) type of the argument of testMap.get(Object)
, and doesn't see the need to perform any coercion since Integer
is-an Object
.
Accessing HashMap in EL with string + int key
This code can accomplish desired behavior
<c:forEach begin="1" end="5" var="current">
<c:set var="key" value="elem-${current}" />
<c:out value="${myHashMap[key]}"/>
</c:forEach>
Accessing Map of Map by a valid key gives NPE
A couple of null checks are needed to prevent NPE, however in both cases this can be resolved with the help of Map::getOrDefault
method.
Also, the type and assignment to year
should be simplified. Integer.valueOf
is needed only if balance.getYear()
returns a string value, otherwise it would be ok to have it as Integer year = balance.getYear();
for (AnnualLeaveBalance balance: annualLeaveBalanceList) {
Map<Integer, Integer> yearToCountMap = employeeYearWiseLeaveAppCount.getOrDefault(
balance.getEmployeeId(), Collections.emptyMap()
); // empty map is returned if no yearToCount map is found for a user
Integer year = balance.getYear();
int takenLeave = yearToCountMap.getOrDefault(year, 0); // no leave taken
}
LinkedHashMap assign to new map with different key
Such operation is generally called "mapping" or "transforming" a collection. It applies not only to maps, but to lists and other collections as well.
As you only need to map keys and keep values intact, you can use mapKeys()
function in Kotlin:
val input = mapOf(1 to "foo", 2 to "bar")
val result = input.mapKeys { it.key.toString() }
Replace toString()
with whatever you use to convert int to a string.
Related Topics
In Java, What Is a Shallow Copy
Why Invoke Thread.Currentthread.Interrupt() in a Catch Interruptexception Block
Adding N Hours to a Date in Java
Java Gui Frameworks. What to Choose? Swing, Swt, Awt, Swingx, Jgoodies, Javafx, Apache Pivot
How to Set Specific Java Version to Maven
How to Convert a String to a Date Using Simpledateformat
How to Get the X and Y of a Program Window in Java
Should I Return a Collection or a Stream
Reliable File.Renameto() Alternative on Windows
Constructor Overloading in Java - Best Practice
Rationale for Matcher Throwing Illegalstateexception When No 'Matching' Method Is Called
Jvm Takes a Long Time to Resolve Ip-Address for Localhost
How to Format Decimals in a Currency Format
Difference Between Java Se/Ee/Me
Splitting String with Pipe Character ("|")
No Suitable Driver Found for 'Jdbc:Mysql://Localhost:3306/Mysql
Correctly Implementing the MVC Pattern in Gui Development Using Swing in Java