分享

Java高质量代码之 — 数组与集合(2)

 dinghj 2013-08-26

前言:由于上一个星期工作繁忙,利用上下班和晚上睡前空余的时间拜读了秦小波老师的《改善Java程序的151建议》,感觉廓然开朗,注意到了很多平时在编写代码中并不会注意的问题,甚至感觉自己对Java只是略懂皮毛,不足以登大雅之堂,特此与读者分享读书笔记,以下内容摘自《改善Java程序的151建议》一书和笔者的理解


Java高质量代码系列文章
      面向对象篇:http://ray-yui./blog/1926984
      数据类型篇:http://ray-yui./blog/1927251
          字符串篇:http://ray-yui./blog/1927647
      数组与集合(1):http://ray-yui./blog/1928170
      数组与集合(2):http://ray-yui./blog/1930155


好吧,本来是非常繁忙的星期五,但笔者公司的DB服务器又down了,文章时间又来了,上一章由于时间限制只讲了一半,下面继续书接上回


10.子列表只是原列表的视图
      List接口中,提供了subList方法,作用就是返回一个列表中的子列表,这个方法与String类型的substring有点类似,请观察以下代码

Java代码  收藏代码
  1. public static void main(String[] args) {  
  2.         List<Integer> list = new ArrayList<Integer>();  
  3.         list.add(1);  
  4.         list.add(2);  
  5.         List<Integer> subList = list.subList(0, 2);  
  6.         System.out.println(subList); // 输出为1,2  
  7.         subList.add(3); // 子列表增加元素  
  8.         System.out.println(list); // 输出原列表,输出为1,2,3  
  9.     }  


      我们可以把List接口中的subList想成和数据库中的视图一样,在数据库中虽然不建议我们对视图进行增删改,但确实是可以操作的,而且操作完也可以反映到表当中,subList也同理,它只是原列表的视图,所有的操作都将直接影响原列表,但subList和数据库视图有一点不同,下面将会进行说明


11.推荐使用subList处理局部列表
      现在有一个需求,在一个长度为50的列表中删除位置是10~20的元素

Java代码  收藏代码
  1. public static void main(String[] args) {  
  2.         List<Integer> list = new ArrayList<Integer>();  
  3.         // 增加50行  
  4.         for (int i = 0, size = 50; i < size; i++) {  
  5.             list.add(i);  
  6.         }  
  7.           
  8.         // 方法1  
  9.         for (int i = 10; i < 20; i++) {  
  10.             list.remove(10);  
  11.         }  
  12.           
  13.         // 方法2  
  14.         list.subList(10, 20).clear();  
  15.         System.out.println(list);  
  16.     }  


      以上方法2,为什么要不停删除位置为10的元素呢?这是很容易忽略的问题,因为List当删除元素时,为了保持有序性,会移动元素替换被删除的位置,所以当位置为10的元素删除后,11的位置会替换上来..在上面已经提到,修改子列表会直接影响原列表,使用方法2的一行就能完成,是不是很方便?


12.生成子列表后不要再操作原列表
      subList生成子列表是原列表的视图,而对子列表的改动会直接影响原列表,那若然是对原列表的更改呢?

Java代码  收藏代码
  1. public static void main(String[] args) {  
  2.         List<Integer> list = new ArrayList<Integer>();  
  3.         list.add(1);  
  4.         list.add(2);  
  5.         List<Integer> subList = list.subList(0, 2);  
  6.         // 原列表增加数据  
  7.         list.add(3);  
  8.         // 对比元素数量  
  9.         System.out.println(list.size() == subList.size());  
  10.   
  11.         // 运行结果,抛出java.util.ConcurrentModificationException异常  
  12.     }  


      看到这个异常,很多人会觉得很郁闷,并发修改异常?可我程序并没有运行在多线程的环境当中,而原因是因为,当创建subList时,list会将1个变量modCount传递到subList的构造参数当中,而当list进行修改时,就会对modCount进行累加,而subList在进行操作时,会检查modCount是否和list的一致,若然不一直就抛出异常,这就是原因,这也是subList和数据库视图不同的地方,表修改后直接反应到视图,而list修改后,不会反应到subList,所以在使用subList时,应该使用如下方式对list进行加锁

Java代码  收藏代码
  1. public static void main(String[] args) {  
  2.         List<Integer> list = new ArrayList<Integer>();  
  3.         list.add(1);  
  4.         list.add(2);  
  5.         List<Integer> subList = list.subList(0, 2);  
  6.         // 设置列表为只读状态  
  7.         list = Collections.unmodifiableList(list);  
  8.         // 再对列表进行操作  
  9.         list.add(4);  
  10.           
  11.         //运行结果,抛出java.lang.UnsupportedOperationException异常  
  12.     }  


      使用以上方式就可以明确表示,此列表是只读,当对列表进行操作时,就会抛出异常,增强了语义,而一个List可以有多个subList,但只要存在一个subList,就不能对原List进行操作.请谨记


13.使用Comparator进行排序
      我们都知道,在集合类中要排序时,可以使用JDK提供的帮助工具,例如
Collections中的sort方法对List进行排序,亦可以使用Tree的数据结构来进行排序,例如Set接口中的TreeSet,TreeMap,使用Tree结构来进行排序时,是需要对象拥有比较性的,我们可以在实体类中实现Comparable<T>接口,但排序的方式是多变的,有时经过一段时间后,需求变了,需要按另外一个元素进行排序,但修改了已经稳定的实体类是不应该的,我们可以使用Comparator来进行排序,请看以下代码

Java代码  收藏代码
  1. public static void main(String[] args) {  
  2.   
  3.         // 使用匿名内部类方式实现  
  4.         List<Integer> list = new ArrayList<Integer>();  
  5.         Collections.sort(list, new Comparator<Integer>() {  
  6.   
  7.             @Override  
  8.             public int compare(Integer o1, Integer o2) {  
  9.                 // TODO Auto-generated method stub  
  10.                 return 0;  
  11.             }  
  12.         });  
  13.   
  14.         // TreeMap实现  
  15.         Map<String, Integer> treeMap = new TreeMap<String, Integer>(  
  16.                 new Comparator<String>() {  
  17.                     @Override  
  18.                     public int compare(String o1, String o2) {  
  19.                         return 0;  
  20.                     }  
  21.                 });  
  22.     }  


      无论是Collections的sort方法和TreeMap,TreeSet都会有重载的方式,接受一个Comparator(比较器),此时有可以在这里进行元素比较的定义


14.使用binarySearch需要注意
      binarySearch(二分查找)是性能较高的一种查找方式,通常我们使用的indexOf为顺序查找,当查找到匹配的值时马上返回,但若然此集合的元素数量多的话,使用顺序查找就性能就会降低,特别是当要查找的元素位于集合的末尾时,效率更低,而二分查找是将数据折半然后进行查找,没有找到指定元素时再折半,但需要注意,二分查找必须保持集合的有序性,所以在开发当中,若要使用二分查找,需先将集合进行排序.这点务必注意


15.集合中元素equlas和compareTo必须同步
      在刚才提及的binarySearch和indexOf两种查找当中,有一点是非常不同的,indexOf的查找的时候是使用equlas来进行匹配,equlas返回为true时认为找到元素,而binarySearch是使用compareTo进行匹配,当compareTo返回0时认为找到元素,而当一个用户实体当中,equlas是使用用户名来重写equlas判断,而compareTo使用密码来比较,那将会导致不必要的错误


16.集合运算时,使用更优雅的方式
      在集合的操作当中,经常会出现两个集合的 并集(or),交集(and),差集(not)等的操作,当然我们可以遍历两个集合,对比元素求出我们需要的并集交集差集等运算的结果,但这种方式处理,真的是最方便?最简单?最优雅?请看以下代码

Java代码  收藏代码
  1. public static void main(String[] args) {  
  2.         List<Integer> list1 = new ArrayList<Integer>();  
  3.         List<Integer> list2 = new ArrayList<Integer>();  
  4.   
  5.         // 并集操作  
  6.         list1.addAll(list2);  
  7.   
  8.         // 交集操作,retainAll会删除list1中没有出现在list2中的元素  
  9.         list1.retainAll(list2);  
  10.   
  11.         // 差集操作  
  12.         list1.removeAll(list2);  
  13.   
  14.         // 无重复并集  
  15.         list2.removeAll(list1);  
  16.         list1.addAll(list2);  
  17.     }  


17.使用shuffle打乱列表
      现在我们有一个需求,做一个扑克游戏,54张扑克存储到一个集合当中,每次发牌之前,都需要打乱一下扑克的顺序,这样的需求,我们可以有多种实现方式,使用Random来随机调整集合位置,或者集合的位置顺序交换,可有更优雅的方式吗?

Java代码  收藏代码
  1. public static void main(String[] args) {  
  2.         List<Integer> list = new ArrayList<Integer>();  
  3.         // 你好,我是one-line君  
  4.         Collections.shuffle(list);  
  5.     }  


18.多线程时使用Vetor或HashTable
      通过查看JDK API可以发现,Vetor和HashTable是JDK1.0时已存在的类,他们是线程安全的,可在多线程的环境下使用


总结:
      笔者在本文章中只从《改善Java程序的151建议》中提取部分进行归纳性叙述,推荐各位读者购买这本书,该书不仅从事例中学习,而且涉及到原理,底层的实现,不仅告诉你应该怎么做,还告诉你为什么要这样做.  

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约