正在加载

Java集合中的集:Set

在上次一起探讨了Java集合中的迭代器Iterator后,现在我们可以继续探讨Set集合。

通过JDK我们可以知道,Set也是一个接口,他实现了Collection和Iterable两个接口,因此,Set是一个集合,同时,我们也可以使用迭代器遍历Set。

Set是一个接口,我们当然不能实例化接口,所以,我们一般实际使用的Set有:

  • HashSet
  • LinkedHashSet
  • TreeSet

HashSet

HashSet存储元素的策略,是使用一中称为“散列表”的数据结构,因此,HashSet中元素的遍历是没有顺序的。而通过散列表来存储元素时,都必须首先调用元素的hashCode方法来判断,得出的值我们叫做哈希码。当我们像HashSet存储一个元素的时候,HashSet会首先检查元素的哈希码,如果哈希码指向的位置为空,则元素可以加进去;如果指向的位置不为空,这就说明了HashSet中有某个元素与这个元素的哈希码相同,这时候就调用equal方法进行两个元素的比较,如果比较的结果不相同,此元素也可以加进HashSet;如果equals比较的结果还是相同,那么就不能添加此元素。也就是说,每次添加元素都会进行hashCode方法的调用,而不一定会调用equals方法,这就大大的提高了程序的效率。同时,这也告诉我们,如果我们自己定义的类要使用HashSet来管理对象,就一定要重写hashCode和equals方法。重写的原则就是保证相同的对象返回的哈希码是相同的,equals返回的值是true。

使用HashSet的优点就是:查询效率特别的高,而且在增删元素的时候,效率也很高,因为是通过哈希码来实现的。
 使用HashSet的缺点就是:使用的空间比较大,这是为了避免散列冲突。

LinkedHashSet

其实和HashSet差不多,就是在HashSet之前加了一个Linked,也就是说,他还是实现了元素的顺序,而不像HashSet的随机。其实,LinkedHashSet就是HashSet的子类。与HashSet不同的就是,LinkedHashSet还有一个双重列表(即Linked)。也就是说,我们使用Iterator遍历LinkedHashSet的时候,不再是随机的,而是按照我们添加元素的顺序。

TreeSet

TreeSet其实跟HashSet差不多,区别如下:

  1. 对于TreeSet的遍历,是有顺序而言的,而且这个顺序是我们可以控制的;而对HashSet遍历,则是没有顺序的。
  2. TreeSet增删元素的速度比HashSet慢。这一点在小数量的元素中看不错来,但是在大量的元素中,就比较明显。

刚刚说了,我们可以对于TreeSet的遍历顺序进行控制,那么是怎么控制的呢?其实,就是在我们自定义的类中实现compareTo方法。此方法回来一个 int 类型的整数。如果返回负值,则排在后面,如果返回正值,则在前面,如果返回0,则两个元素相等,而Set是不允许有重复的元素的,所以这点也保证了Set中元素的唯一性。

以上,便是我们一般使用的Set。

Java集合中的迭代器:Iterator

在上一次的Java中的集合容器中,大概的列举了一些常用的集合,这篇日志中,大家再一起探讨如何遍历集合容器。

如何遍历集合,就要使用到Iterator类了。通过JDK知道,在集合中,我们可以调用iterator()方法,以此来获得Iterator接口。

然后,我们就可以通过Iterator来遍历集合了。Iterator定义了三个方法:

方法摘要
boolean hasNext()
如果仍有元素可以迭代,则返回 true
E next()
返回迭代的下一个元素。
void remove()
从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。

这样,我们就可以调用next()来遍历集合中的元素。

但是,如果达到了集合的最后一个元素遍历之后,将会抛出一个名为NoSuchElementException的异常。所以,Iterator又提供了hasNext()的方法,此方法的主要作用就是为了防止此异常的发生。所以,我们在使用Iterator的时候,经常同时使用以下两个方法,像下面的代码:

List list = new ArrayList();
Iterator iter = list.iterator();
String str;
while( iter.hasNext() ){
    str = iter.next();
    System.out.println( str );
}

有心的读者可能已经注意到,Iterator接口中还定义了一个remove()方法。一般来讲,Iterator中的remove()和Collection中的remove(Object o)是有区别的,具体的源码分析可以看这里

从上面的连接中得知,Iterator中的remove()是间接的使用了容器中的remove(Object o)的。但是,它又维持了程序的正常。还有一点需要注意。那就是在遍历集合中,不能使用容器的remove(Object o)方法。看一下代码:

List list = new ArrayList();
Iterator iter = list.iterator();
String str;
while( iter.hasNext() ){
    str = iter.next();
    System.out.println( str );
    list.remove(str)  //报错,会抛出ConcurrentModificationException异常。因为在遍历的时候,集合不允许被修改。除非由iterator自己修改。
    iter.remove();    //正常,但是需要在next()使用后才能使用
    iter.remove();    //报错,会抛出IllegalStateException异常,因为remove()已经使用过一次了,需再次调用next()方法后才能使用。
}

以上,便是iterator的大概使用方法。

答网友米奇问-20081229

今天,收到了网友米奇的一封邮件,这是一道关于jQuery的问题,大意如下:

HTML部分类似这样:

  • 案例1
  • 案例2
    • 子菜单1
    • 子菜单2
    • 子菜单3

下面主要是jQuery的源码

$(document).ready(function(){
		$("li:has(ul)").click(function(e){
			if(this==e.target){
				if($(this).children().is(":hidden"))   {$(this).children().show();}
					else{$(this).children().hide();}
			}
        return false;
        }).click();     

	});

说明:我无法理解为什么需要对this==e.target进行判断;其实无论是之后结尾的触发click()还是添加CSS来加载时候隐藏效果都是一样的。

触发click()实际的target也是

  • 。我想知道的是为什么要加判断this==e.target;还有就是后面的return false语句。其实 return和判断this==e.target完全都不需要的,为什么要加呢?

    现在回答如下:

    1. 为什么需要对this==e.target进行判断?

    :e是指触发事件发生的元素。如果进行this==e.target进行判断,那么,如果点击子菜单1的时候,子菜单会hide的。而如果进行了this==e.target判断,则子菜单不会hide。那么,究竟是为什么呢?

    这涉及到事件捕获和事件冒泡这两个概念(点这里了解)。不过在这里可以说得更加简单一点。

    当你点击案例2的时候,子菜单显示了,这是毋庸置疑的,因为你已经点击了一个包含ul的li元素。然后,最重要的是,当你点击子菜单1的时候,实际上是点击了四个不同层次的元素,分别是:最顶层的ul,带有ul的li,li下的ul,最底层的li。而点击的事件是发生在最底层的li,也就是说e.target是最底层的li。而this,则是代表这带有ul的li。这意味着,如果没有 this==e.target 这个判断,点击了 子菜单一,还是会触发程序中的click事件。

    2.为什么要加 return false 语句?

    :这个问题我找了挺久的,终于在这里找到了答案。大意就是说,在少代码的页面中,可能不会出现什么问题。但是在写多了代码的情况下,可能会出现一些莫名其妙的内容。

    所以,在后面加上return false 应该是一个良好的习惯。

  • Java中的集合容器

    作为存放对象的容器,集合在Java程序的开发中非常有用。这个容器就像一个篮子,或者是一个水桶。如果你的对象是水,那就使用水桶这个容器;如果你的对象是水果等需要通风的东西,那就使用篮子这个容器。总之,这些容器就是集合,就是用来存放对象,而且可以针对不同的对象,选择不同的容器。

    在 java.util 中,有两个接口,一个是Collection,另一个是Map。

    • Collection。是把元素保存至容器中,主要包括:
      • List。以一定的顺序保存元素,元素可以重复。主要包括:
        • ArrayList。读快,添删慢。
        • LinkedList。读慢,添删快。
      • Set。元素不能重复,也就是数学中的集合概念。主要包括:
        • HashSet。查找的速度快
        • TreeSet。按照元素的升序存放
        • LinkedHashSet。按照存放的顺序排序,但是查找速度也很快
      • Queeu。即队列,也就是先进先出的容器。
    • Map。是把元素以 键-值 的形式保存起来。主要包括:
      • HashMap。查找的速度是最快的。
      • TreeMap。它保存元素的顺序是按照键的升序结果。
      • LinkedHashMap。按照保存的顺序存放元素,同时,查找的速度和HashMap的速度是差不多的。

    这就是 Java 中我们常用的集合容器。

    Hello world!

    Hello world!一般,这都是学习一门新语言的所打印出来的习惯。当然,我也不例外咯!

    建立这个博客的目的也很简单,就是总结自己的学习。由于自己马上就要变成一名程序员了,所以也在积极的学习程序,软件。把自己的学习总结在这里。

    之所以博客的域名为 nupt.org ,是因为自己之前的一个博客《听者有心》域名为:nupt.org.cn。因此,这个博客名为《说者无意》,大概也就很自然了。意思是,这个博客所写的文章是自己学习的总结,因此,如果哪里写错了,也不要怪我 :-)

    毕竟,说者无意嘛。

    PS. nupt.org 这个域名的注册,还要感谢大麦的及时提醒!