??xml version="1.0" encoding="utf-8" standalone="yes"?> 基本的方法,|上到处都是Q在 java 中就是在 web.xml 注册一?/span> Listener Q如下: <listener> <listener-class>xp.web.SessionCounter</listener-class> </listener> SessionCounter.java 实现 javax.servlet.http.HttpSessionListener 接口Q分别在 sessionCreated Ҏ?/span> sessionDestroyed Ҏ中处?/span> session 数目?/span> q样的方法有一定的问题Q?/span> 1 、对于真正从|页讉K的和搜烦引擎?/span> spider 无法区分?/span> 2 、当 Tomcat 重启Ӟ加蝲了上ơ持久化?/span> session Ӟ无法准确计算在线数?/span> W二个问题我们可以不予考虑Q这?/span> tomcat 容器实现不标准的问题Q我们要解决的是的第一个问题,如何知道你的讉K的是真实的?/span> ?/span> js l过搜烦引擎 Q?/span> 做过 pv l计的都知道Q可以用 script 的方式得C真实?/span> pageView 数目Q我们现在要做的是q样的一件事情,我们在所有的面都加入一D话Q?/span> <script type="text/javascript"> document.write ("<iframe src='/sessionCountServlet' width=0 height=0 frameborder=no border=0 MARGINWIDTH=0 MARGINHEIGHT=0 SCROLLING=no></iframe>"); </script> 然后我们写上一?/span> servlet 来记录这些真正的讉K者?/span> import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class SessionCounterServlet extends HttpServlet { public SessionCounterServlet() { super(); } public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { process(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { process(request, response); } public void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { SessionCounter.put(request.getSession().getId()); } } 我们可以看到q个 servlet 只是做了一件事情,?/span> process 里面做了 SessionCounter.put(request.getSession().getId()); q个动作?/span> 我们来看看我们的 SessionCounter 做了些什么: import javax.servlet.http.*; import java.util.Hashtable; public class SessionCounter implements HttpSessionListener { public SessionCounter() { } public static Hashtable m_real = new Hashtable(); private static long count = 0; public void sessionCreated(HttpSessionEvent e) { count++; } public void sessionDestroyed(HttpSessionEvent e) { if (count > 0) { count--; } m_real.remove(e.getSession().getId()); } public static long getSessionCount() { return count; } public static void put(String sessionId){ m_real.put(sessionId,"1"); } public static int getRealCount(){ return m_real.size(); } } 我们记录了一个静态的 hash 表来记录Ȁzȝ态的 sessionid Qƈ?/span> session 销毁的时候将q个 sessionid |ؓI?/span> 怎么?/span> servlet 配置?/span> web 应用中我׃|唆了?/span>
1、test.html 试?/strong>
<html>
<head>
<title>试面</title>
<style>
.list {
border-top:1 solid #8A2BE2;
border-left:1 solid #8A2BE2;
border-right:1 solid #8A2BE2;
}
.list td {
border-bottom: 1 solid #8A2BE2;
}
</style>
<script>
function $(el) {
return document.getElementById(el);
}
function showWin(param) {
window.showModalDialog("dailog.htm", param, "dialogWidth:" +param.width +"px;dialogHeight:"+param.height+"px;center:yes;help:no;scroll:no;status:no;resizable:no");
}
function TB(tbid) {
this.tb = typeof(tbid) == "string"? $(tbid): tbid;
this.getValue = function(rowIndex, cellIndex){
var trs = this.tb.rows[rowIndex];
var _td = trs.cells[cellIndex];
return _td.innerText;
}
this.setValue = function(rowIndex, cellIndex, value) {
var _tr = this.tb.rows[rowIndex];
var _td = _tr.cells[cellIndex];
_td.innerText = value;
}
/********获取行烦?*******/
this.findRowIndex = function(eventSrc) {
var _tr = eventSrc; //eventSrc事g?必须在TD里获事g源是TD或TR本n
while(_tr.tagName != "TR") {
_tr = _tr.parentNode;
}
var trs = this.tb.rows;
for(var i = 0; i < trs.length; i++){
if(_tr == trs[i]) return i;
}
}
}
function edit() {
var tb = new TB("data");
rIndex = tb.findRowIndex(event.srcElement);
$("updateRowIndex").value = rIndex;
$("userName").value = tb.getValue(rIndex, 1); //获得姓名
$("sex").value = tb.getValue(rIndex, 2); //获得性别
$("age").value = tb.getValue(rIndex, 3); //获得q龄
showWin({title:"修改用户信息", width:390, height:230, _div:"openWin",parent:window});
}
function saveAndUpdateView(){
var updateRowIndex = $("updateRowIndex").value;
var tb = new TB($f("data")); //$f()在dailog.html定义,获到的table是父H口中的table
tb.setValue(updateRowIndex, 1, $("userName").value);
tb.setValue(updateRowIndex, 2, $("sex").value);
tb.setValue(updateRowIndex, 3, $("age").value);
close();
}
</script>
</head>
<body>
<p style="margin-top:60px">
<center>
<table id="data" class="list" width="460px">
<tr>
<td>~号</td>
<td>用户?lt;/td>
<td>性别</td>
<td>q龄</td>
<td>操作</td>
</tr>
<tr>
<td>1</td>
<td>李永?lt;/td>
<td>?lt;/td>
<td>27</td>
<td><span style="background:#FAEBD7;cursor:hand" onclick="edit();"> 修改 </span></td>
</tr>
<tr>
<td>2</td>
<td>林兄</td>
<td>?lt;/td>
<td>27</td>
<td><span style="background:#FAEBD7;cursor:hand" onclick="edit();"> 修改 </span></td>
</tr>
<tr>
<td>3</td>
<td>叶兄</td>
<td>?lt;/td>
<td>23</td>
<td><span style="background:#FAEBD7;cursor:hand" onclick="edit();"> 修改 </span></td>
</tr>
</table>
</center>
</p>
<!---弹出H口昄的内?--->
<div id="openWin" style="display:none;">
<form>
<fieldSet>
<legend>修改用户</legend>
<table>
<tr>
<td>用户?lt;/td><td><input type="text" id="userName"/></td>
</tr>
<tr>
<td>性别</td><td><input type="text" id="sex"/></td>
</tr>
<tr>
<td>q龄</td><td><input type="text" id="age"/></td>
</tr>
</table>
</fieldSet>
<input type="hidden" id="updateRowIndex"/>
</form>
<span style="background:#FAEBD7;cursor:hand" onclick="saveAndUpdateView();"> 修改 </span>
</div>
</body>
</html>
2、dailog.html H口原型
<html>
<head>
<script>
var param = window.dialogArguments; //传过来的模式对话框窗口参?br />
document.title = param.title; //H口标题,必须在窗口创建前实现s
/********父H口的js加蝲q来********/
var scripts = param.parent.document.scripts;
var _head = document.getElementsByTagName("head")[0];
for(var n = 0; n < scripts.length; n++) {
if(scripts[n].src) {
var _script = newEl("script");
_script.src = scripts[n].src;
bind(_head, _script);
}else{//加蝲直接在html文档中写的script
var _script = newEl("script");
_script.text = scripts[n].text;
bind(_head, _script);
}
}
/*******ҎID获得父窗口的元素*********/
function $f(el) {
return param.parent.document.getElementById(el);
}
/***********创徏一个HTML元素*******/
function newEl(tagName) {
return document.createElement(tagName);
}
/***********q加元素***************/
function bind(ower, child) {
ower.appendChild(child);
}
/*******在浏览器完成对象的装载后立即触发*********/
window.onload = function() {
var winDiv;
if(typeof(param._div) == "string") {
winDiv = param.parent.document.getElementById(param._div); //父窗口window对象,因ؓparam._div对象在父H口
}else{//直接传对象过?br />
winDiv = param._div;
}
$("mainDiv").innerHTML = winDiv.innerHTML; //DIV内容在弹出窗口中渲染
}
</script>
</head>
<body>
<center>
<div id="mainDiv" style="margin-top:20px;width:90%"></div>
</center>
</body>
</html>
]]>
基本ҎQ?/span>
]]>
1.Single Table Strategy ,单表{略,一张表包含基类与子cȝ所有数?很多情况下都是采用这L冗余设计,通过一个discriminator来区?/span>
2.Table Per Class Strategy ,每个子类对应一张表,每张表都拥有基类的属?/span>
3.Join Strategy ,仍然是每个子cd应一张表Q但此表中不包含基类的属?仅仅是此子类的扩展属?׃n基类的属?/span>
以一个例子来说明3U情?
一.单表{略
比如Pet作ؓ基类,Cat和Dogl承此类q拥有自q扩展属??
package com.denny_blue.ejb3.inheritance;
import java.io.Serializable;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "animal_type", discriminatorType = DiscriminatorType.STRING)
public class Pet implements Serializable {
private int id;
private String name;
private double weight;
public Pet() {
}
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
}
Petcd的注意的就是通过@Inheritance(strategy = InheritanceType.SINGLE_TABLE)定采用单表{略,通过@DiscriminatorColumn定了标志值的字段和类型,我想熟悉hibernate的朋友对q些都应该很熟悉.然后是两个子c?
//Cat.java
package com.denny_blue.ejb3.inheritance;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(discriminatorType = DiscriminatorType.STRING)
@DiscriminatorValue("cat")
public class Cat extends Pet {
private String HairBall;
public String getHairBall() {
return HairBall;
}
public void setHairBall(String hairBall) {
HairBall = hairBall;
}
}
//Dog.java
package com.denny_blue.ejb3.inheritance;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(discriminatorType=DiscriminatorType.STRING)
@DiscriminatorValue("dog")
public class Dog extends Pet {
private String trick;
public String getTrick() {
return trick;
}
public void setTrick(String trick) {
this.trick = trick;
}
}
两个子类最值的x的就是@DiscriminatorValue注释,比如Cat的此gؓcat,意味着当Catcd的Entity存入数据库时,JPA自动把cat的Dlanimal_type字段,Dog的值则为dog,由此可以在同一张表中区分开两个不同的子c?
?Table per Class
采用Table Per Class{略的话Q每个子c都单独徏表,q且都独立拥有基cM的所有属?互相之间不共?在我们的例子中所要进行的修改很小,像这?
//基类
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Pet implements Serializable {
private int id;
private String name;
private double weight;
........
//子类:不需要Q何设|?/span>
@Entity
public class Dog extends Pet {
private String trick;
.......
.......
?Join{略
每个子类同样独立,基类也独立徏表,只不q所有的子类的表中只有扩展属?他们׃n基类的表,在我们的例子中修改下卛_:
//基类
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Pet implements Serializable {
private int id;
private String name;
private double weight;
........
//子类
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Dog extends Pet {
private String trick;
.......
.......
q部分的内容实在没什么新?与hibernate完全一?JAVA EE5向spring和hibernate借鉴了太多东?
{}
1. 使用表的别名(Alias)
当在SQL语句中连接多个表Ӟ 请用表的别名ƈ把别名前~于每个Column上。这样一来,可以减解析的旉q减那些由Column歧义引v的语法错误?br />
(Column歧义指的是由于SQL中不同的表具有相同的Column名,当SQL语句中出现这个ColumnӞSQL解析器无法判断这个Column的归?
2. 用EXISTS替代IN
在许多基于基表的查询中,Z满一个条Ӟ往往需要对另一个表q行联接。在q种情况下, 使用EXISTS(或NOT EXISTS)通常提高查询的效率?br />
低效Q?br />
SELECT*
FROMEMP(基础?
WHEREEMPNO>0
ANDDEPTNOIN(SELECTDEPTNO
FROMDEPT
WHERELOC=‘MELB’)
高效Q?br />
SELECT*
FROMEMP(基础?
WHEREEMPNO>0
ANDEXISTS(SELECT‘X’
FROMDEPT
WHEREDEPT.DEPTNO=EMP.DEPTNO
ANDLOC=‘MELB’)
(相对来说Q用NOT EXISTS替换NOT IN 更显著地提高效率,下面指?
3. 用NOT EXISTS替代NOT IN
在子查询中,NOT IN子句执行一个内部的排序和合q?无论在哪U情况下QNOT IN都是最低效?(因ؓ它对子查询中的表执行了一个全表遍??Z避免使用NOT IN Q我们可以把它改写成外连?Outer Joins)或NOT EXISTS.
例如Q?br />
SELECT…
FROMEMP
WHEREDEPT_NONOTIN(SELECTDEPT_NO
FROMDEPT
WHEREDEPT_CAT=’A’);
Z提高效率。改写ؓQ?br />
(Ҏ一Q?高效)
SELECT….
FROMEMPA,DEPTB
WHEREA.DEPT_NO=B.DEPT(+)
ANDB.DEPT_NOISNULL
ANDB.DEPT_CAT(+)=‘A’
1. 用EXPLAIN PLAN 分析SQL语句
EXPLAIN PLAN 是一个很好的分析SQL语句的工P它甚臛_以在不执行SQL的情况下分析语句?通过分析Q我们就可以知道ORACLE是怎么栯接表Q用什么方式扫描表(索引扫描或全表扫?以及使用到的索引名称?/p>
你需要按照从里到外,从上C的次序解d析的l果?EXPLAIN PLAN分析的结果是用羃q的格式排列的, 最内部的操作将被最先解读, 如果两个操作处于同一层中Q带有最操作号的将被首先执行?/p>
NESTED LOOP是少C按照上述规则处理的操作, 正确的执行\径是查对NESTED LOOP提供数据的操作,其中操作h的被最先处理?/p>
通过实践Q?感到q是用SQLPLUS中的SET TRACE 功能比较方便?/p>
举例Q?/p>
SQL> list
1 SELECT *
2 FROM dept, emp
3* WHERE emp.deptno = dept.deptno
SQL> set autotrace traceonly /*traceonly 可以不显C执行结?/
SQL> /
14 rows selected.
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 NESTED LOOPS
2 1 TABLE ACCESS (FULL) OF 'EMP'
3 1 TABLE ACCESS (BY INDEX ROWID) OF 'DEPT'
4 3 INDEX (UNIQUE SCAN) OF 'PK_DEPT' (UNIQUE)
Statistics
----------------------------------------------------------
0 recursive calls
2 db block gets
30 consistent gets
0 physical reads
0 redo size
2598 bytes sent via SQL*Net to client
503 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
14 rows processed
通过以上分析Q可以得出实际的执行步骤是:
1. TABLE ACCESS (FULL) OF 'EMP'
2. INDEX (UNIQUE SCAN) OF 'PK_DEPT' (UNIQUE)
3. TABLE ACCESS (BY INDEX ROWID) OF 'DEPT'
4. NESTED LOOPS (JOINING 1 AND 3)
注: 目前许多W三方的工具如TOAD和ORACLE本n提供的工具如OMS的SQL Analyze都提供了极其方便的EXPLAIN PLAN工具。也许喜Ƣ图形化界面的朋友们可以选用它们?/p>
2. 用烦引提高效?/p>
索引是表的一个概念部分,用来提高索数据的效率?实际上,ORACLE使用了一个复杂的自^衡B-treel构?通常Q通过索引查询数据比全表扫描要快?当ORACLE扑և执行查询和Update语句的最佌\径时Q?ORACLE优化器将使用索引?同样在联l多个表时用烦引也可以提高效率?另一个用烦引的好处是,它提供了主键(primary key)的唯一性验证?/p>
除了那些LONG或LONG RAW数据cdQ?你可以烦引几乎所有的列?通常Q?在大型表中用烦引特别有效?当然Q你也会发现Q?在扫描小表时Q用烦引同栯提高效率?/p>
虽然使用索引能得到查询效率的提高Q但是我们也必须注意到它的代仗?索引需要空间来存储Q也需要定期维护, 每当有记录在表中增减或烦引列被修ҎQ?索引本n也会被修攏V?q意味着每条记录的INSERT Q?DELETE Q?UPDATEؓ此多付出4 Q?5 ơ的盘I/O . 因ؓ索引需要额外的存储I间和处理,那些不必要的索引反而会使查询反应时间变慢?/p>
定期的重构烦引是有必要的?/p>
ALTER INDEX REBUILD
3. 索引的操?/p>
ORACLE对烦引有两种讉K模式?/p>
索引唯一扫描 ( INDEX UNIQUE SCAN)
大多数情况下Q?优化器通过WHERE子句讉KINDEX.
表LODGING有两个烦?Q?建立在LODGING列上的唯一性烦引LODGING_PK和徏立在MANAGER列上的非唯一性烦引LODGING$MANAGER.
SELECT*
FROMLODGING
WHERELODGING=‘ROSEHILL’;
在内?Q?上述SQL被分成两步执行Q?首先 Q?LODGING_PK 索引通过索引唯一扫描的方式被讉K Q?获得相对应的ROWIDQ?通过ROWID讉K表的方式执行下一步检索?/p>
如果被检索返回的列包括在INDEX列中QORACLE不执行W二步的处理(通过ROWID讉K??因ؓ索数据保存在索引中, 单单讉K索引可以完全满x询结果?/p>
下面SQL只需要INDEX UNIQUE SCAN 操作?/p>
SELECTLODGING
FROMLODGING
WHERELODGING=‘ROSEHILL’;
索引范围查询(INDEX RANGE SCAN)
适用于两U情况:
1. Z一个范围的?/p>
2. Z非唯一性烦引的?/p>
?Q?/p>
SELECT LODGING FROM LODGING WHERE LODGING LIKE ‘M%’;
WHERE子句条g包括一pd| ORACLE通过索引范围查询的方式查询LODGING_PK . ׃索引范围查询返回一l| 它的效率p比烦引唯一扫描低一些?/p>
?Q?/p>
SELECTLODGING
FROMLODGING
WHEREMANAGER=‘BILLGATES’;
q个SQL的执行分两步Q?LODGING$MANAGER的烦引范围查?得到所有符合条件记录的ROWID) 和下一步同qROWID讉K表得到LODGING列的倹{?׃LODGING$MANAGER是一个非唯一性的索引Q数据库不能对它执行索引唯一扫描?/p>
׃SQLq回LODGING列,而它q不存在于LODGING$MANAGER索引中, 所以在索引范围查询后会执行一个通过ROWID讉K表的操作?/p>
WHERE子句中, 如果索引列所对应的值的W一个字W由通配W?WILDCARD)开始, 索引不被采用。在q种情况下,ORACLE用全表扫描?/p>
SELECTLODGING
FROMLODGING
WHEREMANAGERLIKE‘%HANMAN’;
1. 基础表的选择
基础?Driving Table)是指被最先访问的?通常以全表扫描的方式被访??Ҏ优化器的不同Q?SQL语句中基表的选择是不一L?/p>
如果你用的是CBO (COST BASED OPTIMIZER)Q优化器会检查SQL语句中的每个表的物理大小Q烦引的状态,然后选用p最低的执行路径?/p>
如果你用RBO (RULE BASED OPTIMIZER) Q?q且所有的q接条g都有索引对应Q?在这U情况下Q?基础表就是FROM 子句中列在最后的那个表?/p>
举例Q?/p>
SELECTA.NAMEQB.MANAGER
FROM WORKERAQ?br />
LODGINGB
WHERE A.LODGING=B.LODING;
׃LODGING表的LODING列上有一个烦引, 而且WORKER表中没有相比较的索引Q?WORKER表将被作为查询中的基表?/p>
2. 多个q等的烦?/p>
当SQL语句的执行\径可以用分布在多个表上的多个烦引时Q?ORACLE会同时用多个烦引ƈ在运行时对它们的记录q行合ƈQ?索出仅对全部索引有效的记录?/p>
在ORACLE选择执行路径Ӟ唯一性烦引的{高于非唯一性烦引?然而这个规则只有当WHERE子句中烦引列和常量比较才有效。如果烦引列和其他表的烦引类相比较?q种子句在优化器中的{是非怽的?/p>
如果不同表中两个惛_{的烦引将被引用, FROM子句中表的顺序将军_哪个会被率先使用?FROM子句中最后的表的索引有最高的优先U?/p>
如果相同表中两个惛_{的烦引将被引用, WHERE子句中最先被引用的烦引将有最高的优先U?/p>
举例Q?/p>
DEPTNO上有一个非唯一性烦引,EMP_CAT也有一个非唯一性烦引?/p>
SELECTENAMEQ?br />
FROMEMP
WHEREDEPT_NO=20
ANDEMP_CAT=‘A’;
q里QDEPTNO索引被最先检索,然后同EMP_CAT索引索出的记录进行合q?执行路径如下Q?br />
TABLEACCESSBYROWIDONEMP
AND-EQUAL
INDEXRANGESCANONDEPT_IDX
INDEXRANGESCANONCAT_IDX
3. {式比较和范围比?br />
当WHERE子句中有索引列, ORACLE不能合ƈ它们QORACLE用范围比较?br />
举例Q?br />
DEPTNO上有一个非唯一性烦引,EMP_CAT也有一个非唯一性烦引?br />
SELECTENAME
FROMEMP
WHEREDEPTNO>20
ANDEMP_CAT=‘A’;
q里只有EMP_CAT索引被用刎ͼ然后所有的记录逐条与DEPTNO条gq行比较?执行路径如下Q?br />
TABLEACCESSBYROWIDONEMP
INDEXRANGESCANONCAT_IDX
4. 不明的索引{
当ORACLE无法判断索引的等U高低差别,优化器将只用一个烦引,它就是在WHERE子句中被列在最前面的?br />
举例Q?br />
DEPTNO上有一个非唯一性烦引,EMP_CAT也有一个非唯一性烦引?br />
SELECTENAME
FROMEMP
WHEREDEPTNO>20
ANDEMP_CAT>‘A’;
q里Q?ORACLE只用CDEPT_NO索引?执行路径如下Q?br />
TABLEACCESSBYROWIDONEMP
INDEXRANGESCANONDEPT_IDX
译者按Q我们来试一下以下这U情况:
SQL> select index_nameQ?uniqueness from user_indexes where table_name = 'EMP'Q?br />
INDEX_NAME UNIQUENES
------------------------------ ---------
EMPNO UNIQUE
EMPTYPE NONUNIQUE
SQL> select * from emp where empno >= 2 and emp_type = 'A' Q?br />
no rows selected
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS QBY INDEX ROWIDQ?OF 'EMP'
2 1 INDEX QRANGE SCANQ?OF 'EMPTYPE' QNON-UNIQUE
虽然EMPNO是唯一性烦引,但是׃它所做的是范围比较, {要比非唯一性烦引的{式比较?
5. 强制索引失效
如果两个或以上烦引具有相同的{Q你可以强制命oORACLE优化器用其中的一?通过它,索出的记录数量少) .
举例Q?br />
SELECTENAME
FROMEMP
WHEREEMPNO=7935
ANDDEPTNO+0=10/*DEPTNO上的索引失?/
ANDEMP_TYPE||‘’=‘A’/*EMP_TYPE上的索引失?/
q是一U相当直接的提高查询效率的办法?但是你必}慎考虑q种{略Q一般来_只有在你希望单独优化几个SQL时才能采用它?br />
q里有一个例子关于何旉用这U策略,
假设在EMP表的EMP_TYPE列上有一个非唯一性的索引而EMP_CLASS上没有烦引?br />
SELECTENAME
FROMEMP
WHEREEMP_TYPE=‘A’
ANDEMP_CLASS=‘X’;
优化器会注意到EMP_TYPE上的索引q用它?q是目前唯一的选择?如果Q一D|间以后, 另一个非唯一性徏立在EMP_CLASS上,优化器必d两个索引q行选择Q在通常情况下,优化器将使用两个索引q在他们的结果集合上执行排序及合q?然而,如果其中一个烦?EMP_TYPE)接近于唯一性而另一个烦?EMP_CLASS)上有几千个重复的倹{?排序及合q就会成ZU不必要的负担?在这U情况下Q你希望使优化器屏蔽掉EMP_CLASS索引?br />
用下面的Ҏ可以解决问题?br />
SELECTENAME
FROMEMP
WHEREEMP_TYPE=‘A’
ANDEMP_CLASS||‘’=‘X’;
1. 避免在烦引列上用计?
WHERE子句中,如果索引列是函数的一部分。优化器不使用索引而用全表扫描?/p>
举例Q?/p>
低效Q?/p>
SELECT…
FROMDEPT
WHERESAL*12>25000;
高效Q?/p>
SELECT…
FROMDEPT
WHERESAL>25000/12;
Q这是一个非常实用的规则Q请务必牢记
2. 自动选择索引
如果表中有两个以?包括两个)索引Q其中有一个唯一性烦引,而其他是非唯一性?/p>
在这U情况下QORACLE用唯一性烦引而完全忽略非唯一性烦引?/p>
举例Q?/p>
SELECTENAME
FROMEMP
WHEREEMPNO=2326
ANDDEPTNO=20;
q里Q只有EMPNO上的索引是唯一性的Q所以EMPNO索引用来检索记录?/p>
TABLEACCESSBYROWIDONEMP
INDEXUNIQUESCANONEMP_NO_IDX
3. 避免在烦引列上用NOT
通常Q我们要避免在烦引列上用NOTQ?NOT会生在和在索引列上使用函数相同的媄响?当ORACLE“遇到”NOTQ他׃停止使用索引转而执行全表扫描?/p>
举例Q?/p>
低效Q?(q里Q不使用索引)
SELECT…
FROMDEPT
WHEREDEPT_CODENOT=0;
高效Q?(q里Q用了索引)
SELECT…
FROMDEPT
WHEREDEPT_CODE>0;
需要注意的是,在某些时候, ORACLE优化器会自动NOT转化成相对应的关pL作符?/p>
NOT > to <=
NOT >= to <
NOT < to >=
NOT <= to >
Q在q个例子中,作者犯了一些错误?例子中的低效率SQL是不能被执行的?/p>
我做了一些测试:
SQL> select * from emp where NOT empno > 1Q?br />
no rows selected
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS QBY INDEX ROWIDQ?OF 'EMP'
2 1 INDEX QRANGE SCANQ?OF 'EMPNO' QUNIQUEQ?br />
SQL> select * from emp where empno <= 1Q?br />
no rows selected
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS QBY INDEX ROWIDQ?OF 'EMP'
2 1 INDEX QRANGE SCANQ?OF 'EMPNO' QUNIQUEQ?/p>
两者的效率完全一P也许q符合作者关?#8220; 在某些时候, ORACLE优化器会自动NOT转化成相对应的关pL作符” 的观炏V?/p>
4. ?gt;=替代>
如果DEPTNO上有一个烦引,
高效Q?/p>
SELECT*
FROMEMP
WHEREDEPTNO>=4
低效Q?/p>
SELECT*
FROMEMP
WHEREDEPTNO>3
两者的区别在于Q?前者DBMS直接蟩到第一个DEPT{于4的记录而后者将首先定位到DEPTNO=3的记录ƈ且向前扫描到W一个DEPT大于3的记录?br />
1. 避免在烦引列上用计?
WHERE子句中,如果索引列是函数的一部分。优化器不使用索引而用全表扫描?/p>
举例Q?/p>
低效Q?/p>
SELECT…
FROMDEPT
WHERESAL*12>25000;
高效Q?/p>
SELECT…
FROMDEPT
WHERESAL>25000/12;
Q这是一个非常实用的规则Q请务必牢记
2. 自动选择索引
如果表中有两个以?包括两个)索引Q其中有一个唯一性烦引,而其他是非唯一性?/p>
在这U情况下QORACLE用唯一性烦引而完全忽略非唯一性烦引?/p>
举例Q?/p>
SELECTENAME
FROMEMP
WHEREEMPNO=2326
ANDDEPTNO=20;
q里Q只有EMPNO上的索引是唯一性的Q所以EMPNO索引用来检索记录?/p>
TABLEACCESSBYROWIDONEMP
INDEXUNIQUESCANONEMP_NO_IDX
3. 避免在烦引列上用NOT
通常Q我们要避免在烦引列上用NOTQ?NOT会生在和在索引列上使用函数相同的媄响?当ORACLE“遇到”NOTQ他׃停止使用索引转而执行全表扫描?/p>
举例Q?/p>
低效Q?(q里Q不使用索引)
SELECT…
FROMDEPT
WHEREDEPT_CODENOT=0;
高效Q?(q里Q用了索引)
SELECT…
FROMDEPT
WHEREDEPT_CODE>0;
需要注意的是,在某些时候, ORACLE优化器会自动NOT转化成相对应的关pL作符?/p>
NOT > to <=
NOT >= to <
NOT < to >=
NOT <= to >
Q在q个例子中,作者犯了一些错误?例子中的低效率SQL是不能被执行的?/p>
我做了一些测试:
SQL> select * from emp where NOT empno > 1Q?br />
no rows selected
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS QBY INDEX ROWIDQ?OF 'EMP'
2 1 INDEX QRANGE SCANQ?OF 'EMPNO' QUNIQUEQ?br />
SQL> select * from emp where empno <= 1Q?br />
no rows selected
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS QBY INDEX ROWIDQ?OF 'EMP'
2 1 INDEX QRANGE SCANQ?OF 'EMPNO' QUNIQUEQ?/p>
两者的效率完全一P也许q符合作者关?#8220; 在某些时候, ORACLE优化器会自动NOT转化成相对应的关pL作符” 的观炏V?/p>
4. ?gt;=替代>
如果DEPTNO上有一个烦引,
高效Q?/p>
SELECT*
FROMEMP
WHEREDEPTNO>=4
低效Q?/p>
SELECT*
FROMEMP
WHEREDEPTNO>3
两者的区别在于Q?前者DBMS直接蟩到第一个DEPT{于4的记录而后者将首先定位到DEPTNO=3的记录ƈ且向前扫描到W一个DEPT大于3的记录?br />
{http://blog.chinaunix.net/u/20483/showart_546882.html}
HashMap是Java新Collection Framework中用来代替HashTable的一个实玎ͼHashMap和HashTable的区别是Q?HashMap是未l同步的Q而且允许null倹{HashTablel承DictionaryQ而且使用了EnumerationQ所以被不要使用?br />HashMap的声明如下:
public class HashMap extends AbstractMap implements Map, Cloneable,Serializable
有关AbstractMapQ?a >http://blog.csdn.net/treeroot/archive/2004/09/20/110343.aspx
有关MapQ?a >http://blog.csdn.net/treeroot/archive/2004/09/20/110331.aspx
有关CloneableQ?a >http://blog.csdn.net/treeroot/archive/2004/09/07/96936.aspx
q个cL较复杂,q里只是重点分析了几个方法,特别是后面涉及到很多内部c都没有解释
不过都比较简单?/p>
static final int DEFAULT_INITIAL_CAPACITY = 16; 默认初始化大?/p>
static final int MAXIMUM_CAPACITY = 1 << 30; 最大初始化大小
static final float DEFAULT_LOAD_FACTOR = 0.75f; 默认加蝲因子
transient Entry[] table; 一个Entrycd的数l,数组的长度ؓ2的指数?/p>
transient int size; 映射的个?/p>
int threshold; 下一ơ扩Ҏ的?/p>
final float loadFactor; 加蝲因子
transient volatile int modCount; 修改ơ数
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +loadFactor);
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
this.loadFactor = loadFactor;
threshold = (int)(capacity * loadFactor);
table = new Entry[capacity];
init();
}
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
threshold = (int)(DEFAULT_INITIAL_CAPACITY);
注意Q这里应该是一个失误! 应该是:threshold =(int)(DEFAULT_INITIAL_CAPACITY * loadFactor);
table = new Entry[DEFAULT_INITIAL_CAPACITY];
init();
}
public HashMap(Map m) {
this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
putAllForCreate(m);
}
void init() {}
static final Object NULL_KEY = new Object();
static Object maskNull(Object key){
return (key == null ? NULL_KEY : key);
}
static Object unmaskNull(Object key) {
return (key == NULL_KEY ? null : key);
}
static int hash(Object x) {
int h = x.hashCode();
h += ~(h << 9);
h ^= (h >>> 14);
h += (h << 4);
h ^= (h >>> 10);
return h;
}
在HashTable中没有这个方法,也就是说HashTable中是直接用对象的hashCode|但是HashMap做了改进 用这个算法来获得哈希倹{?/p>
static boolean eq(Object x, Object y) {
return x == y || x.equals(y);
}
static int indexFor(int h, int length) {
return h & (length-1);
}
Ҏ哈希值和数组的长度来q回该hash值在数组中的位置Q只是简单的与关pR?/p>
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
public Object get(Object key) {
Object k = maskNull(key);
int hash = hash(k);
int i = indexFor(hash, table.length);
Entry e = table[i];
while (true) {
if (e == null) return e;
if (e.hash == hash && eq(k, e.key)) return e.value;
e = e.next;
}
}
q个Ҏ是获取数据的ҎQ首先获得哈希|q里把null值掩CQƈ且hash值经q函数hash()修正?然后计算该哈希值在数组中的索引倹{如果该索引处的引用为nullQ表CHashMap中不存在q个映射?否则的话遍历整个链表Q这里找Cp?如果没有扑ֈ遍历到链表末尾Q返回null。这里的比较是这LQe.hash==hash && eq(k,e.key) 也就是说如果hash不同p定认Z相等Qeqp短\了,只有?hash相同的情况下才调用equalsҎ。现在我们该明白Object中说的如果两个对象equalsq回trueQ他们的 hashCode应该相同的道理了吧。假如两个对象调用equalsq回trueQ但是hashCode不一P那么在HashMap 里就认ؓ他们不相{?/span>
public boolean containsKey(Object key) {
Object k = maskNull(key);
int hash = hash(k);
int i = indexFor(hash, table.length);
Entry e = table[i];
while (e != null) {
if (e.hash == hash && eq(k, e.key)) return true;
e = e.next;
}
return false;
}
q个Ҏ比上面的单,先找到哈希位|,再遍历整个链表,如果扑ֈp回true?/p>Entry getEntry(Object key) {
Object k = maskNull(key);
int hash = hash(k);
int i = indexFor(hash, table.length);
Entry e = table[i];
while (e != null && !(e.hash == hash && eq(k, e.key)))
e = e.next;
return e;
}
q个ҎҎkeyD回Entry节点Q也是先获得索引位置Q再遍历链表Q如果没有找到返回的是null?
public Object put(Object key, Object value) {
Object k = maskNull(key);
int hash = hash(k);
int i = indexFor(hash, table.length);
for (Entry e = table[i]; e != null; e = e.next) {
if (e.hash == hash && eq(k, e.key)) {
Object oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, k, value, i);
return null;
}
首先获得hash索引位置Q如果该位置的引用ؓnullQ那么直接插入一个映,q回null。如果此处的引用不是nullQ必遍历链表,如果扑ֈ一个相同的keyQ那么就更新该valueQ同时返回原来的value倹{如果遍历完了没有找刎ͼ说明该keyg存在Q还是插入一个映。如果hashD够离散的话,也就是说该烦引没有被使用的话Q那么不不用遍历链表了。相反,如果hashgLQ极端的说如果是常数的话Q所有的映射都会在这一个链表上Q效率会极其低下。这里D一个最单的例子Q写?br />个不同的cM为key插入到HashMap中,效率会远q不同?br />class Good{
int i;
public Good(int i){
this.i=i;
}
public boolean equals(Object o){
return (o instanceof Good) && (this.i==((Good)o).i)
}
public int hashCode(){
return i;
}
}
class Bad{
int i;
public Good(int i){
this.i=i;
}
public boolean equals(Object o){
return (o instanceof Good) && (this.i==((Good)o).i)
}
public int hashCode(){
return 0;
}
}
执行代码Q?br />Map m1=new HashMap();
Map m2=new HashMap();
for(int i=0;i<100;i++){
m1.put(new Good(i),new Integer(i)); //q里效率非常?br />}
for(int i=0;i<100;i++){
m2.put(new Bad(i),new Integer(i)); //q里几乎要崩?br />}
上面的是两个非常极端的例子,执行一下就知道差别有多大?/span>
private void putForCreate(Object key, Object value) {
Object k = maskNull(key);
int hash = hash(k);
int i = indexFor(hash, table.length);
for (Entry e = table[i]; e != null; e = e.next) {
if (e.hash == hash && eq(k, e.key)) {
e.value = value;
return;
}
}
createEntry(hash, k, value, i);
}
void putAllForCreate(Map m) {
for (Iterator i = m.entrySet().iterator(); i.hasNext(); ) {
Map.Entry e = (Map.Entry) i.next();
putForCreate(e.getKey(), e.getValue());
}
}
上面的两个方法是被构造函数和cloneҎ调用的?/p>
void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (size < threshold || oldCapacity > newCapacity)
return;
Entry[] newTable = new Entry[newCapacity];
transfer(newTable);
table = newTable;
threshold = (int)(newCapacity * loadFactor);
}
q个Ҏ在需要的时候重新分配空_相当于ArrayList的ensureCapacityҎQ不q这个更加复杂?br />
void transfer(Entry[] newTable) {
Entry[] src = table;
int newCapacity = newTable.length;
for (int j = 0; j < src.length; j++) {
Entry e = src[j];
if (e != null) {
src[j] = null;
do {
Entry next = e.next;
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
} while (e != null);
}
}
}
遍历原来的数l,如果该Entry不是null的话Q说明有映射Q然后遍历这个链表,把所有的映射插入到新的数l中Q注意这里要从新计算索引位置?/p>
public void putAll(Map t) {
int n = t.size();
if (n == 0)
return;
if (n >= threshold) {
n = (int)(n / loadFactor + 1);
if (n > MAXIMUM_CAPACITY)
n = MAXIMUM_CAPACITY;
int capacity = table.length;
while (capacity < n) capacity <<= 1;
resize(capacity);
}
for (Iterator i = t.entrySet().iterator(); i.hasNext(); ) {
Map.Entry e = (Map.Entry) i.next();
put(e.getKey(), e.getValue());
}
}
q个Ҏ先确定是否需要扩大空_然后循环调用putҎ?/p>
public Object remove(Object key) {
Entry e = removeEntryForKey(key);
return (e == null ? e : e.value);
}
Entry removeEntryForKey(Object key) {
Object k = maskNull(key);
int hash = hash(k);
int i = indexFor(hash, table.length);
Entry prev = table[i];
Entry e = prev;
while (e != null) { 如果e==null表示不存?br /> Entry next = e.next;
if (e.hash == hash && eq(k, e.key)) {
modCount++;
size--;
if (prev == e)
table[i] = next; 链表的第一个元素就是要删除的,q里最好加一?e.next=null.
else
prev.next = next; 存在担不是链表的W一个元素, q里最好加一?e.next=null.
e.recordRemoval(this);
return e;
}
prev = e;
e = next;
}
return e; q里其实是return null;
}
q个Ҏ其实也不复杂Q也是遍历链表,q里加一句e.next=null,可以改ؓ
if(prev==e)
table[i]=next;
else
prev.next=next;
e.next=null; q一句是多加的,可以提高效率?br />q里单说明我的看法:
因ؓe是被删除的节点,删除它其实就是指向它的指针指向它的后面一个节炏V所以e可以作ؓGC回收的对象?br />可以eq有一个next指针指向我们的数据,如果e没有被回收。而且此时e.next指向的节点也变ؓ没用的了Q但?br />却有一个它的引?e.next),所以虽然e的下一个节Ҏ用了Q但是却不能作ؓGC回收的对象,除非e先被回收?br />虽然不一定会引v很大的问题,但是臛_会媄响GC的回收效率。就像数据库中的外键引用一P删除h很麻烦呀?
Entry removeMapping(Object o) {
if (!(o instanceof Map.Entry))
return null;
Map.Entry entry = (Map.Entry)o;
Object k = maskNull(entry.getKey());
int hash = hash(k);
int i = indexFor(hash, table.length);
Entry prev = table[i];
Entry e = prev;
while (e != null) {
Entry next = e.next;
if (e.hash == hash && e.equals(entry)) {
modCount++;
size--;
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval(this);
return e;
}
prev = e;
e = next;
}
return e;
}
q个Ҏ和上面的一栗?/p>
public void clear() {
modCount++;
Entry tab[] = table;
for (int i = 0; i < tab.length; i++)
tab[i] = null;
size = 0;
}
同样可以改进
public boolean containsValue(Object value) {
if (value == null)
return containsNullValue();
Entry tab[] = table;
for (int i = 0; i < tab.length ; i++)
for (Entry e = tab[i] ; e != null ; e = e.next)
if (value.equals(e.value)) return true;
return false;
}
private boolean containsNullValue() {
Entry tab[] = table;
for (int i = 0; i < tab.length ; i++)
for (Entry e = tab[i] ; e != null ; e = e.next)
if (e.value == null) return true;
return false;
}
public Object clone() {
HashMap result = null;
try {
result = (HashMap)super.clone();
}
catch (CloneNotSupportedException e) { // assert false; }
result.table = new Entry[table.length];
result.entrySet = null;
result.modCount = 0;
result.size = 0;
result.init();
result.putAllForCreate(this);
return result;
}
static class Entry implements Map.Entry {
final Object key;
Object value;
final int hash;
Entry next;
Entry(int h, Object k, Object v, Entry n) {
value = v;
next = n;
key = k;
hash = h;
}
public Object getKey() {
return unmaskNull(key);
}
public Object getValue() {
return value;
}
public Object setValue(Object newValue) {
Object oldValue = value;
value = newValue;
return oldValue;
}
public boolean equals(Object o) {
if (!(o instanceof Map.Entry)) return false;
Map.Entry e = (Map.Entry)o;
Object k1 = getKey();
Object k2 = e.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
Object v1 = getValue();
Object v2 = e.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2))) return true;
}
return false;
}
public int hashCode() {
return (key==NULL_KEY ? 0 : key.hashCode()) ^ (value==null ? 0 : value.hashCode());
}
public String toString() {
return getKey() + "=" + getValue();
}
void recordAccess(HashMap m) { }
void recordRemoval(HashMap m) { }
}
一个静态内部类
void addEntry(int hash, Object key, Object value, int bucketIndex) {
table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);
if (size++ >= threshold)
resize(2 * table.length);
}
注意q个ҎQ插入连表的头?br />可以写成q样更好理解Q?br />Entry oldHead=table[bucketIndex];
Entry newHead = new Entry(hash,key,value,oldHead);
table[bucketIndex]=newHead;
void createEntry(int hash, Object key, Object value, int bucketIndex) {
table[bucketIndex] = new Entry(hash, key, value, table[bucketIndex]);
size++;
}
private abstract class HashIterator implements Iterator {
Entry next;
int expectedModCount;
int index;
Entry current;
HashIterator() {
expectedModCount = modCount;
Entry[] t = table;
int i = t.length;
Entry n = null;
if (size != 0) {
while (i > 0 && (n = t[--i]) == null) ;
}
next = n;
index = i;
}
public boolean hasNext() {
return next != null;
}
Entry nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Entry e = next;
if (e == null)
throw new NoSuchElementException();
Entry n = e.next;
Entry[] t = table;
int i = index;
while (n == null && i > 0)
n = t[--i]; index = i;
next = n;
return current = e;
}
public void remove() {
if (current == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
Object k = current.key;
current = null;
HashMap.this.removeEntryForKey(k);
expectedModCount = modCount;
}
}
private class ValueIterator extends HashIterator {
public Object next() {
return nextEntry().value;
}
}
private class KeyIterator extends HashIterator {
public Object next() {
return nextEntry().getKey();
}
}
private class EntryIterator extends HashIterator {
public Object next() {
return nextEntry();
}
}
Iterator newKeyIterator() {
return new KeyIterator();
}
Iterator newValueIterator() {
return new ValueIterator();
}
Iterator newEntryIterator() {
return new EntryIterator();
}
private transient Set entrySet = null;
public Set keySet() {
Set ks = keySet;
return (ks != null ? ks : (keySet = new KeySet()));
}
private class KeySet extends AbstractSet {
public Iterator iterator() {
return newKeyIterator();
}
public int size() {
return size;
}
public boolean contains(Object o) {
return containsKey(o);
}
public boolean remove(Object o) {
return HashMap.this.removeEntryForKey(o) != null;
}
public void clear() {
HashMap.this.clear();
}
}
public Collection values() {
Collection vs = values; return (vs != null ? vs : (values = new Values()));
}
private class Values extends AbstractCollection {
public Iterator iterator() {
return newValueIterator();
}
public int size() {
return size;
}
public boolean contains(Object o) {
return containsValue(o);
}
public void clear() {
HashMap.this.clear();
}
}
public Set entrySet() {
Set es = entrySet;
return (es != null ? es : (entrySet = new EntrySet()));
}
private class EntrySet extends AbstractSet {
public Iterator iterator() {
return newEntryIterator();
}
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry e = (Map.Entry)o;
Entry candidate = getEntry(e.getKey());
return candidate != null && candidate.equals(e);
}
public boolean remove(Object o) {
return removeMapping(o) != null;
}
public int size() {
return size;
}
public void clear() {
HashMap.this.clear();
}
}
private void writeObject(java.io.ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
s.writeInt(table.length);
s.writeInt(size);
for (Iterator i = entrySet().iterator(); i.hasNext(); ) {
Map.Entry e = (Map.Entry) i.next();
s.writeObject(e.getKey());
s.writeObject(e.getValue());
}
}
private static final long serialVersionUID = 362498820763181265L;
private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
s.defaultReadObject();
int numBuckets = s.readInt();
table = new Entry[numBuckets];
init();
size = s.readInt(); for (int i=0;
for (int i=0; i<size; i++) {
Object key = s.readObject();
Object value = s.readObject();
putForCreate(key, value);
}
}
int capacity() {
return table.length;
}
float loadFactor() {
return loadFactor;
}