Java高质量代码之 — 数组与集合(2)_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > Java高质量代码之 — 数组与集合(2)

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

 2013/8/23 18:58:26  ray_yui  程序员俱乐部  我要评论(0)
  • 摘要:前言:由于上一个星期工作繁忙,利用上下班和晚上睡前空余的时间拜读了秦小波老师的《改善Java程序的151建议》,感觉廓然开朗,注意到了很多平时在编写代码中并不会注意的问题,甚至感觉自己对Java只是略懂皮毛,不足以登大雅之堂,特此与读者分享读书笔记,以下内容摘自《改善Java程序的151建议》一书和笔者的理解Java高质量代码系列文章面向对象篇:http://ray-yui.iteye.com/blog/1926984数据类型篇:http://ray-yui.iteye
  • 标签:

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


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


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


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

class="java">
public static void main(String[] args) {
		List<Integer> list = new ArrayList<Integer>();
		list.add(1);
		list.add(2);
		List<Integer> subList = list.subList(0, 2);
		System.out.println(subList); // 输出为1,2
		subList.add(3); // 子列表增加元素
		System.out.println(list); // 输出原列表,输出为1,2,3
	}


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


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

public static void main(String[] args) {
		List<Integer> list = new ArrayList<Integer>();
		// 增加50行
		for (int i = 0, size = 50; i < size; i++) {
			list.add(i);
		}
		
		// 方法1
		for (int i = 10; i < 20; i++) {
			list.remove(10);
		}
		
		// 方法2
		list.subList(10, 20).clear();
		System.out.println(list);
	}


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


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

public static void main(String[] args) {
		List<Integer> list = new ArrayList<Integer>();
		list.add(1);
		list.add(2);
		List<Integer> subList = list.subList(0, 2);
		// 原列表增加数据
		list.add(3);
		// 对比元素数量
		System.out.println(list.size() == subList.size());

		// 运行结果,抛出java.util.ConcurrentModificationException异常
	}


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

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


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


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

public static void main(String[] args) {

		// 使用匿名内部类方式实现
		List<Integer> list = new ArrayList<Integer>();
		Collections.sort(list, new Comparator<Integer>() {

			@Override
			public int compare(Integer o1, Integer o2) {
				// TODO Auto-generated method stub
				return 0;
			}
		});

		// TreeMap实现
		Map<String, Integer> treeMap = new TreeMap<String, Integer>(
				new Comparator<String>() {
					@Override
					public int compare(String o1, String o2) {
						return 0;
					}
				});
	}


      无论是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)等的操作,当然我们可以遍历两个集合,对比元素求出我们需要的并集交集差集等运算的结果,但这种方式处理,真的是最方便?最简单?最优雅?请看以下代码

public static void main(String[] args) {
		List<Integer> list1 = new ArrayList<Integer>();
		List<Integer> list2 = new ArrayList<Integer>();

		// 并集操作
		list1.addAll(list2);

		// 交集操作,retainAll会删除list1中没有出现在list2中的元素
		list1.retainAll(list2);

		// 差集操作
		list1.removeAll(list2);

		// 无重复并集
		list2.removeAll(list1);
		list1.addAll(list2);
	}


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

public static void main(String[] args) {
		List<Integer> list = new ArrayList<Integer>();
		// 你好,我是one-line君
		Collections.shuffle(list);
	}


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


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