`
swanky_yao
  • 浏览: 30814 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

看高手代码--从小case学大道理

阅读更多
今天看sun的HttpMessages.java文件,虽然文件很小,但是对我的启发很大。
前面定义了大量的HTTP常量:
    ...
    ...
    private static final String STATUS_305 = "Use Proxy";
    private static final String STATUS_307 = "Temporary Redirect";
    private static final String STATUS_400 = "Bad Request";
    private static final String STATUS_401 = "Unauthorized";
    private static final String STATUS_402 = "Payment Required";
    private static final String STATUS_403 = "Forbidden";
    private static final String STATUS_404 = "Not Found";
    private static final String STATUS_405 = "Method Not Allowed";
    private static final String STATUS_406 = "Not Acceptable";
    private static final String STATUS_407 = "Proxy Authentication Required";
    private static final String STATUS_408 = "Request Timeout";
    ...
    ...

也可以趁机从这里查看HTTP返回值的意义^_^。然后定义了一个java.util.concurrent.ConcurrentHashMap的变量httpStatusCodeMappings,再往里面添加刚才定义的常量(这个操作放在了一个静态块里),如:
static {
        httpStatusCodeMappings.put("sc.100", STATUS_100);
        httpStatusCodeMappings.put("sc.101", STATUS_101);
        httpStatusCodeMappings.put("sc.200", STATUS_200);
        httpStatusCodeMappings.put("sc.201", STATUS_201);
        httpStatusCodeMappings.put("sc.202", STATUS_202);
        httpStatusCodeMappings.put("sc.203", STATUS_203);
  ...
  ...

最重要的就是这个方法了,先看代码
public static String getMessage(int status) {

        // Return the status message for the most frequently used status
        // codes directly, without any lookup
        switch (status) {
            case 200: return STATUS_200;
            case 302: return STATUS_302;
            case 400: return STATUS_400;
            case 404: return STATUS_404;
	}

        return httpStatusCodeMappings.get("sc."+ status);
    }

这段代码的用途很简单,就是要返回状态码对应的Message,而这些这些消息都已经存放在httpStatusCodeMappings变量里了,为了提高访问常用的几个返回码的Message,它直接用了一个静态块,而不去lookup那个map了,这就是高手!
分享到:
评论
116 楼 kulinglei 2011-03-22  
lubezhang 写道
看到这个帖子,自己也写了一个测试例子,但是结果好像不一样,是不是我的例子写的有问题吗?
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class TestGetMessage {
    private static final String STATUS_305 = "Use Proxy";
    private static final String STATUS_307 = "Temporary Redirect";
    private static final String STATUS_400 = "Bad Request";
    private static final String STATUS_401 = "Unauthorized";
    private static final String STATUS_402 = "Payment Required";
    private static final String STATUS_403 = "Forbidden";
    private static final String STATUS_404 = "Not Found";
    private static final String STATUS_405 = "Method Not Allowed";
    private static final String STATUS_406 = "Not Acceptable";
    private static final String STATUS_407 = "Proxy Authentication Required";
    private static final String STATUS_408 = "Request Timeout";
    
    static Map<String, String> httpStatusCodeMappings = new ConcurrentHashMap<String, String>();
    static {
        httpStatusCodeMappings.put("sc.305", STATUS_305);
        httpStatusCodeMappings.put("sc.307", STATUS_307);
        httpStatusCodeMappings.put("sc.400", STATUS_400);
        httpStatusCodeMappings.put("sc.401", STATUS_401);
        httpStatusCodeMappings.put("sc.402", STATUS_402);
        httpStatusCodeMappings.put("sc.403", STATUS_403);
        httpStatusCodeMappings.put("sc.404", STATUS_404);
        httpStatusCodeMappings.put("sc.405", STATUS_405);
        httpStatusCodeMappings.put("sc.406", STATUS_406);
        httpStatusCodeMappings.put("sc.407", STATUS_407);
        httpStatusCodeMappings.put("sc.408", STATUS_408);
    }
    
    public static String getSwitchMessage(int status){
        switch (status) {
            case 305: return STATUS_305;
            case 404: return STATUS_404;
    	}
		return null; 
    }
    
    public static String getMapMessage(int status){
        return httpStatusCodeMappings.get("sc."+ status);
    }


	public static void main(String[] args) {
		int status = 404;
		long loopNum = 100000000L;
		
		long switchStartTime = System.currentTimeMillis();
		for(long i = 0; i < loopNum; i++){
			getSwitchMessage(status);
		}
		System.out.println("switch:"+(System.currentTimeMillis() - switchStartTime));
		
		long mapStartTime = System.currentTimeMillis();
		for(long i = 0; i < loopNum; i++){
			getSwitchMessage(status);
		}
		System.out.println("map:"+(System.currentTimeMillis() - mapStartTime));
	}
}

错误就不说了,你这样测试时没有意义的
115 楼 kulinglei 2011-03-22  
myumen 写道
抛出异常的爱 写道
pengzhoushuo 写道
好贴,程序员都应该学习。
1、认为HashMap性能比switch高的同学建议学习下数据结构,所谓HashMap就是一个链表散列,一个连续的链表用于保存key,默认的情况下它的长度是16。HashMap对Key做Hash,根据取到的Hash码再找到对应的的key所在的散,再通过查找所在散列里对应Key从而找到Value。这种速度比得上4个case的switch?
2、以可读性作为借口的童鞋们的理由是站不住脚的,加了一个switch就看不懂了?即使是,过错也不在写程序的人,而是读程序的人,注释是写得清清楚楚......
3、说写好代码不如加硬件的童鞋,你觉得自己比不上机器?加硬件那是无耐之举。比如你做了一个OA管理系统,你跟领导说你需要10台高性能的机器才能跑起来,领导会怎么看你?可别跟我说什么云计算。最后套用达文西的话,做人要有良知才行啊,写程序也一样,要对得起自己良心

天地良心.
把代码写好懂点才更对的点别人
少作孽写一堆别人看不懂的代码


哥们,虽然你有5颗星,但这次不得不反对你了。个人觉得:这是一个非常不错的Tips,不至于看不懂吧?我写的话肯定会在switch语句块前注明为什么要这样写。考虑另外一个问题:
		List someList = new ArrayList();
		try {
			someList = someObject.somdMethod();
			// more
		} catch (Exception e) {
			// do sth
		}

		return someList;

new ArrayList()就是没有必要的,当然啦,也不会太影响性能的,也不会产生可读性的问题,但是这样写真的应该吗?作为程序员,本身就应该精益求精,如果实际中我写了我粘上的这种类似的代码,我感到羞愧,不为别的,因为我想做到更好。

在讨论可读性和性能的问题,你这个例子说明了什么,可读性差,性能差,什么都不能说明
lz的代码可读性还可以,一般都会真么做的。
我一直认为可读性大于性能,当然这有个范围。
114 楼 ppgunjack 2011-03-22  
徐风子 写道
skydream 写道
sw1982 写道
...lookup 一下hashmap真的那么低效吗? 建议复习下数据结构哦,你这些总结是没错,可是很表面


典型的没有写过高并发程序的思维方式,明明可以节约的地方,仅仅几行代码就可以优化,偏偏不做。

hashmap再快,也比case 一个 整型满上1w倍。

性能,是一点一点挤牙膏挤出来的,哪能到处浪费啊。

首先,谁说hash表比case慢一万倍的??
其实case的代码实现是 if ..else if形式的,复杂度是O(n)
hashMap 的复杂度是 O(1),当然里面的实现有函数调用,(函数调用的效率我没测试过,不过据《代码大全2》里面的资料,java的函数调用所消耗的时间和 普通赋值语句相同。) 就是说hashMap实际操作时间不过 十几个 语句周期。

代码最大的问题是 明明是一堆静态常量,为什么要用 ConcurrentHashMap??

感觉实际编码的过程是: 开始不知道为什么使用了一个大而全,含义复杂的map,(所以做成同步,key中出现了字符串拼装),后来发现效率不够,于是打补丁,就出现了那堆case。 没什么好说的,不过是一堆临时代码,真正应该做的是重构应该重新定义Map的结构。

其实这种东西效率最高的是 以空间换时间:
String[] httpStatusCode = new int[600];
httpStatusCode[404] = "Not Found";
………………
只是多使用了 600 * 8 = 4.8k空间。

或者最普通的方式,使用:
com.google.common.collect.ImmutableMap


方法是好方法,但case的代码实现是 if ..else 是错误的,深入理解计算机系统里面有明确解释,case背后的代码和ifelse比是数量级的差别尤其在case部分的数值是连续的情况下,和hashmap的差距应该至少可以达到两个数量级
正好最近讨论一个帖子,map的存取已经成为瓶颈

楼上你是代码有问题,实际结果:
switch:4334
map:117842

这个差距实际被return string带来的开销抹平不少,
   
public static int getSwitchMessage(int status){   
        switch (status) {   
            case 305: return 1;   
            case 404: return 2;   
        }   
        return 0;    
    }   
       
    public static int getMapMessage(int status){   
        httpStatusCodeMappings.get("sc."+ status);
        return 0;
    } 

这个开销是:
switch:1399
map:108843

遇到桶内元素大于1,应该还能拉个把数量级
113 楼 swanky_yao 2011-03-22  
lubezhang 写道
看到这个帖子,自己也写了一个测试例子,但是结果好像不一样,是不是我的例子写的有问题吗?
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class TestGetMessage {
    private static final String STATUS_305 = "Use Proxy";
    private static final String STATUS_307 = "Temporary Redirect";
    private static final String STATUS_400 = "Bad Request";
    private static final String STATUS_401 = "Unauthorized";
    private static final String STATUS_402 = "Payment Required";
    private static final String STATUS_403 = "Forbidden";
    private static final String STATUS_404 = "Not Found";
    private static final String STATUS_405 = "Method Not Allowed";
    private static final String STATUS_406 = "Not Acceptable";
    private static final String STATUS_407 = "Proxy Authentication Required";
    private static final String STATUS_408 = "Request Timeout";
    
    static Map<String, String> httpStatusCodeMappings = new ConcurrentHashMap<String, String>();
    static {
        httpStatusCodeMappings.put("sc.305", STATUS_305);
        httpStatusCodeMappings.put("sc.307", STATUS_307);
        httpStatusCodeMappings.put("sc.400", STATUS_400);
        httpStatusCodeMappings.put("sc.401", STATUS_401);
        httpStatusCodeMappings.put("sc.402", STATUS_402);
        httpStatusCodeMappings.put("sc.403", STATUS_403);
        httpStatusCodeMappings.put("sc.404", STATUS_404);
        httpStatusCodeMappings.put("sc.405", STATUS_405);
        httpStatusCodeMappings.put("sc.406", STATUS_406);
        httpStatusCodeMappings.put("sc.407", STATUS_407);
        httpStatusCodeMappings.put("sc.408", STATUS_408);
    }
    
    public static String getSwitchMessage(int status){
        switch (status) {
            case 305: return STATUS_305;
            case 404: return STATUS_404;
    	}
		return null; 
    }
    
    public static String getMapMessage(int status){
        return httpStatusCodeMappings.get("sc."+ status);
    }


	public static void main(String[] args) {
		int status = 404;
		long loopNum = 100000000L;
		
		long switchStartTime = System.currentTimeMillis();
		for(long i = 0; i < loopNum; i++){
			getSwitchMessage(status);
		}
		System.out.println("switch:"+(System.currentTimeMillis() - switchStartTime));
		
		long mapStartTime = System.currentTimeMillis();
		for(long i = 0; i < loopNum; i++){
			getSwitchMessage(status);
		}
		System.out.println("map:"+(System.currentTimeMillis() - mapStartTime));
	}
}

第二个for循环怎么还getSwitchMessage(status);??应该是
getMapMessage(int status)了吧
112 楼 lubezhang 2011-03-22  
看到这个帖子,自己也写了一个测试例子,但是结果好像不一样,是不是我的例子写的有问题吗?
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class TestGetMessage {
    private static final String STATUS_305 = "Use Proxy";
    private static final String STATUS_307 = "Temporary Redirect";
    private static final String STATUS_400 = "Bad Request";
    private static final String STATUS_401 = "Unauthorized";
    private static final String STATUS_402 = "Payment Required";
    private static final String STATUS_403 = "Forbidden";
    private static final String STATUS_404 = "Not Found";
    private static final String STATUS_405 = "Method Not Allowed";
    private static final String STATUS_406 = "Not Acceptable";
    private static final String STATUS_407 = "Proxy Authentication Required";
    private static final String STATUS_408 = "Request Timeout";
    
    static Map<String, String> httpStatusCodeMappings = new ConcurrentHashMap<String, String>();
    static {
        httpStatusCodeMappings.put("sc.305", STATUS_305);
        httpStatusCodeMappings.put("sc.307", STATUS_307);
        httpStatusCodeMappings.put("sc.400", STATUS_400);
        httpStatusCodeMappings.put("sc.401", STATUS_401);
        httpStatusCodeMappings.put("sc.402", STATUS_402);
        httpStatusCodeMappings.put("sc.403", STATUS_403);
        httpStatusCodeMappings.put("sc.404", STATUS_404);
        httpStatusCodeMappings.put("sc.405", STATUS_405);
        httpStatusCodeMappings.put("sc.406", STATUS_406);
        httpStatusCodeMappings.put("sc.407", STATUS_407);
        httpStatusCodeMappings.put("sc.408", STATUS_408);
    }
    
    public static String getSwitchMessage(int status){
        switch (status) {
            case 305: return STATUS_305;
            case 404: return STATUS_404;
    	}
		return null; 
    }
    
    public static String getMapMessage(int status){
        return httpStatusCodeMappings.get("sc."+ status);
    }


	public static void main(String[] args) {
		int status = 404;
		long loopNum = 100000000L;
		
		long switchStartTime = System.currentTimeMillis();
		for(long i = 0; i < loopNum; i++){
			getSwitchMessage(status);
		}
		System.out.println("switch:"+(System.currentTimeMillis() - switchStartTime));
		
		long mapStartTime = System.currentTimeMillis();
		for(long i = 0; i < loopNum; i++){
			getSwitchMessage(status);
		}
		System.out.println("map:"+(System.currentTimeMillis() - mapStartTime));
	}
}
111 楼 killerover85 2011-02-28  
看到你们的贴,偷乐
110 楼 joknm 2011-02-28  
代码写得好还要领导看得懂。这就是悲哀。
109 楼 DAOException 2011-02-28  
抢街饭 写道
我就感觉 sun的东西 一直被模仿 一直没有被超越

108 楼 抢街饭 2011-02-27  
我就感觉 sun的东西 一直被模仿 一直没有被超越
107 楼 zhang_xzhi_xjtu 2011-02-27  
性能提高了。
可读性并没有明显下降。
顶楼主的分享精神。
106 楼 ghostsun 2010-06-24  
抛出异常的爱 写道
mathfox 写道
又一个可以在工作中装x的方法出现了。

少装X 装X遭雷劈

装X从来不会被雷劈,不然我的周围早已电闪雷鸣
105 楼 囧囧有神 2010-06-24  
抛出异常的爱 写道
		
try {
    List someList = someObject.somdMethod();
   // more
    return someList;
} catch (Exception e) {
   // do sth
}		


try {
    List someList = someObject.somdMethod();
   // more
    return someList;
} catch (Exception e) {
   // do sth
   return new ArrayList();
}
104 楼 囧囧有神 2010-06-24  
徐风子 写道
skydream 写道
sw1982 写道
...lookup 一下hashmap真的那么低效吗? 建议复习下数据结构哦,你这些总结是没错,可是很表面


典型的没有写过高并发程序的思维方式,明明可以节约的地方,仅仅几行代码就可以优化,偏偏不做。

hashmap再快,也比case 一个 整型满上1w倍。

性能,是一点一点挤牙膏挤出来的,哪能到处浪费啊。

首先,谁说hash表比case慢一万倍的??
其实case的代码实现是 if ..else if形式的,复杂度是O(n)
hashMap 的复杂度是 O(1),当然里面的实现有函数调用,(函数调用的效率我没测试过,不过据《代码大全2》里面的资料,java的函数调用所消耗的时间和 普通赋值语句相同。) 就是说hashMap实际操作时间不过 十几个 语句周期。

代码最大的问题是 明明是一堆静态常量,为什么要用 ConcurrentHashMap??

感觉实际编码的过程是: 开始不知道为什么使用了一个大而全,含义复杂的map,(所以做成同步,key中出现了字符串拼装),后来发现效率不够,于是打补丁,就出现了那堆case。 没什么好说的,不过是一堆临时代码,真正应该做的是重构应该重新定义Map的结构。

其实这种东西效率最高的是 以空间换时间:
String[] httpStatusCode = new int[600];
httpStatusCode[404] = "Not Found";
………………
只是多使用了 600 * 8 = 4.8k空间。

或者最普通的方式,使用:
com.google.common.collect.ImmutableMap

这个想法比较巧妙
103 楼 抛出异常的爱 2010-06-17  
		
try {
    List someList = someObject.somdMethod();
   // more
    return someList;
} catch (Exception e) {
   // do sth
}

		
102 楼 myumen 2010-06-13  
抛出异常的爱 写道
pengzhoushuo 写道
好贴,程序员都应该学习。
1、认为HashMap性能比switch高的同学建议学习下数据结构,所谓HashMap就是一个链表散列,一个连续的链表用于保存key,默认的情况下它的长度是16。HashMap对Key做Hash,根据取到的Hash码再找到对应的的key所在的散,再通过查找所在散列里对应Key从而找到Value。这种速度比得上4个case的switch?
2、以可读性作为借口的童鞋们的理由是站不住脚的,加了一个switch就看不懂了?即使是,过错也不在写程序的人,而是读程序的人,注释是写得清清楚楚......
3、说写好代码不如加硬件的童鞋,你觉得自己比不上机器?加硬件那是无耐之举。比如你做了一个OA管理系统,你跟领导说你需要10台高性能的机器才能跑起来,领导会怎么看你?可别跟我说什么云计算。最后套用达文西的话,做人要有良知才行啊,写程序也一样,要对得起自己良心

天地良心.
把代码写好懂点才更对的点别人
少作孽写一堆别人看不懂的代码


哥们,虽然你有5颗星,但这次不得不反对你了。个人觉得:这是一个非常不错的Tips,不至于看不懂吧?我写的话肯定会在switch语句块前注明为什么要这样写。考虑另外一个问题:
		List someList = new ArrayList();
		try {
			someList = someObject.somdMethod();
			// more
		} catch (Exception e) {
			// do sth
		}

		return someList;

new ArrayList()就是没有必要的,当然啦,也不会太影响性能的,也不会产生可读性的问题,但是这样写真的应该吗?作为程序员,本身就应该精益求精,如果实际中我写了我粘上的这种类似的代码,我感到羞愧,不为别的,因为我想做到更好。
101 楼 抛出异常的爱 2010-06-13  
pengzhoushuo 写道
好贴,程序员都应该学习。
1、认为HashMap性能比switch高的同学建议学习下数据结构,所谓HashMap就是一个链表散列,一个连续的链表用于保存key,默认的情况下它的长度是16。HashMap对Key做Hash,根据取到的Hash码再找到对应的的key所在的散,再通过查找所在散列里对应Key从而找到Value。这种速度比得上4个case的switch?
2、以可读性作为借口的童鞋们的理由是站不住脚的,加了一个switch就看不懂了?即使是,过错也不在写程序的人,而是读程序的人,注释是写得清清楚楚......
3、说写好代码不如加硬件的童鞋,你觉得自己比不上机器?加硬件那是无耐之举。比如你做了一个OA管理系统,你跟领导说你需要10台高性能的机器才能跑起来,领导会怎么看你?可别跟我说什么云计算。最后套用达文西的话,做人要有良知才行啊,写程序也一样,要对得起自己良心

天地良心.
把代码写好懂点才更对的点别人
少作孽写一堆别人看不懂的代码
100 楼 pengzhoushuo 2010-06-13  
好贴,程序员都应该学习。
1、认为HashMap性能比switch高的同学建议学习下数据结构,所谓HashMap就是一个链表散列,一个连续的链表用于保存key,默认的情况下它的长度是16。HashMap对Key做Hash,根据取到的Hash码再找到对应的的key所在的散,再通过查找所在散列里对应Key从而找到Value。这种速度比得上4个case的switch?
2、以可读性作为借口的童鞋们的理由是站不住脚的,加了一个switch就看不懂了?即使是,过错也不在写程序的人,而是读程序的人,注释是写得清清楚楚......
3、说写好代码不如加硬件的童鞋,你觉得自己比不上机器?加硬件那是无耐之举。比如你做了一个OA管理系统,你跟领导说你需要10台高性能的机器才能跑起来,领导会怎么看你?可别跟我说什么云计算。最后套用达文西的话,做人要有良知才行啊,写程序也一样,要对得起自己良心。
99 楼 对酒当歌,人生几何 2010-06-12  
领导 不需要性能。所以他们都反对。
98 楼 iaimstar 2010-06-09  
hu437 写道
[
建议去看看程序员修炼之道~~


你从哪里看出我没看过

人要学会自己思考,不要拿来就套
97 楼 hu437 2010-06-09  
iaimstar 写道
skydream 写道
sw1982 写道
...lookup 一下hashmap真的那么低效吗? 建议复习下数据结构哦,你这些总结是没错,可是很表面


典型的没有写过高并发程序的思维方式,明明可以节约的地方,仅仅几行代码就可以优化,偏偏不做。

hashmap再快,也比case 一个 整型满上1w倍。

性能,是一点一点挤牙膏挤出来的,哪能到处浪费啊。

一个团队好多个人

指望所有人都挤牙膏,开玩笑

顶多就是核心代码慎重一些

整个项目再整体测试一下性能瓶颈,有针对性的调整就好了

每句代码都想写的句句珠玑,既不可能,也不现实。

在需求允许的范围内,尽量的提高开发效率,才能适时的交出合适的作业

建议去看看程序员修炼之道~~

相关推荐

Global site tag (gtag.js) - Google Analytics