您的位置:首页 > 职场人生

黑马程序员-2-java-Collection集合类知识串讲(1)-List及Set

2015-05-26 17:06 337 查看
</pre><p></p><p style="text-align:center"><strong><span style="font-family:Simsun; font-size:14px">------- </span><a target=_blank target="_blank" href="http://www.itheima.com/" style="font-family:Simsun; font-size:14px">android培训</a><span style="font-family:Simsun; font-size:14px">、</span><a target=_blank target="_blank" href="http://www.itheima.com/" style="font-family:Simsun; font-size:14px">java培训</a><span style="font-family:Simsun; font-size:14px">、期待与您交流! ----------</span></strong></p><p><strong></strong></p><p><strong>集合</strong></p><p>       Collection类接口所定义的集合是单列集合,较之Map类所定义的双列集合,他只有一个类型的可操纵元素。该接口有以下几个常见的声明:</p><p>       Collection接口实现了Iterable接口,该接口主要抽象了iterator()方法</p><p>       add(e):添加至末尾;</p><p>       iterator():生成本对象的迭代器;</p><p>       addAll(c):添加所有至调用对象的末尾;</p><p>       remove(Object):从调用它的对象中,移除对应单个实例; </p><p>       retainAll():取交集,存入调用它的一方</p><p>       addAll():将参数方的所有元素都添加到调用方</p><p>       removeAll():移除调用一方中与参数相同的数据</p><p>       containAll():检查调用方是否包含参数中的所有数据,真则反悔true,反之为false</p><p>       interator():调用迭代器,迭代器是集合取出元素的方式 </p><p>       Interator是一个接口,常用方法:hasNext():集合中是否还有元素;</p><p>       next():提取下一个</p><p>       remove()移除迭代器指向元素</p><p>       注意:next()每调用一次,就得用hasNext()判断一次,避免异常产生 </p><p>       Interator it=a1 . interator():表明interator()函数返回值为Interator接口的一个子类对象,实现了多态。常用的Interator类具有局限性,并不提供添加操作。所以,我们对于复杂操作的迭代器使用根据需求,可选取ListIterator类来完成操作。较之Iterator类,ListIterator类多出了前驱判定、返回、回溯操作,以及对数据的增、改操作。但同时,ListIterator类也只能由List类调用。</p><p>       iterator方法是在接口类Iterable之中的,这是在jdk1.5后,java出现的新特性。提高程序的扩展性。而且,我们有了新的简化使用方法,输出集合类的所有元素:       </p><p></p><pre name="code" class="java">       for(String s : a)
       {
       System.out.println(s);
       }


当然,简化都会导致局限性,这种用法的局限性就是,该方法只能获取元素,而不能对元素进行修改,不能对集合进行操作。这就是他的弊端。这样的用法,必须对元素进行限定(即,如果是集合的话,就不许在上面有声明元素的类型,这样for使用的时候才能有明确的元素类型,共它操作),必须得有便利目标。所以,对数组的便利,还是使用未简化的传统用法比较好。

Collection中为抽象方法,具体实现的列入ArrayList类,ArrayList类继承了AbstractList类,而抽象类AbstractList抽象类实现了对应的interator()接口调用函数。

常用的接口一般使用for循环来写,为的是在循环结束后释放已经使用过的对象资源。

ArrayList 继承自AbstractList类,而AbstractList类实现了Collection接口,AbstractList类提供了interator()迭代器方法,从Collection接口中继承了抽象的增、删、改、查方法。当然,Collection类中只是初步的定义了增和删两个操作,而没有涉及到改、查,对应改、查方法的定义与部分实现,是有AbstractList来完成的,并在他的两个子类ArrayList和LinkedList当中完全实现的。

对应的关系由父到子:Collection->AbstractList->ArrayList



图1.Java中集合类的关系图

List:元素是有序的,因为该集合有索引,元素可以重复。

List类中的特有方法:

set(index , element):改变指定位置的元素为element值

add(index , element):添加元素到指定位置,位于该位置的元素依次向右移动一位,同时,该方法也是List类的独有方法。;

listIterator():生成List类独有的迭代器。常用的Interator类具有局限性,并不提供添加操作。所以,我们对于复杂操作的迭代器使用根据需求,可选取ListIterator类来完成操作。

subList(fron, end):List的独有方法,可以类比subString()方法;

remove(index):移除制定位置的元素;

根据底层数据存储结构的不同,我们把表分为了顺序表、链表。对应的,我们的类也分为了ArrayList类与LinkedList类。而根据底层的数据存储结构的不同,这两个类对于增、删、改、查的速度也是不同的。ArrayList类,查询速度快,增删速度慢;LinkedList类,查询速度慢,增删速度快。

对于这两种类的使用,我们可以根据下表来选择:

表1 ArrayList与LinkedList使用条件简表

情况
主要查询
主要增删
次要查询
ArrayList
优先ArrayList可考虑LinkedList
次要增删
ArrayList
LinkedList
接下来,我们来看对应List类当中的另一个类Vector。对于底层是数组数据结构的数据,我们存储的时候使用的类为Vector类,显而易见,该类为集合框架未出现时的早期版本,现在已被取代。同时,Vector类是同步的(类锁),ArrayList类是不同步的。而如果需要限制访问,我们完全可以对ArrayList类及其对象,根据具体情况进行加锁。因此,Vector类已被集合框架下的ArrayList类所取代。

对应Vector类下的特有方法,addElement、elementAt、removeElement、insertElement、firstElement、lastElement、removeAllElement、setElement、removeElementAt。

其中,最重要的是elements()方法,此方法返回一个此向量组的枚举类,即Enumeration类,此类有两个类独有方法,hasMoreElements()和nextElement(),其作用可以类比迭代器Interator类和ListIterator类在集合框架中的作用

ArrayList类的延长,采用的是50%延长的特点;Vector则是采用的100%,更加的浪费空间。

我们来说一下LinkedList类的不同,他比ArrayList类多出了addFirst、addLast、removeFirst、removeLast、peekFirst、peekLast、offerFirst、offerLast、getFirst、getLast、pollFirst、pollLast,应该善加使用LinkedList类的头尾操作方法,这是最能体现链表特点的应用。其中offer系列、peek系列和poll系列的方法分别是add系列、get系列与remove系列的升级版,避免了NoSuchElementException异常。

需要提的是,对于有限定条件的顺序表结构,即队列和栈,我们分别有对应的方法:

队列:offer、peek、poll;

栈:pop、push。

注意:在使用equals()方法比较自定义类,或复杂的类对象的时候,equals方法只比较对象地址值。可以用 instanceof 来判断类型是否是想要的类型。List方法判断元素是否相同,依据的是元素自身的equals方法。同理,remove(e)、indexOf(e)等这类的包含类型对象的方法,都是调用equals方法来判定索取对象是否是想要的对象的。因此,自定义类的话,如果需要,还是有必要重载equals方法的。

Set:元素是无序的,即存取无序,元素不可重复。

该类为接口。equals()是元素对象的equals函数,在未覆盖的情况下,比较的是元素对象的地址值。所以,两个HashSet类对象中存储的数据,哈希值是可以相同的,但是如果是不同的元素,其地址值是不一样的。

Set类的方法和Collection类是一致的,也就是说Set类没有set、get系列方法。这样就使得我们想要取出元素,就只能采用迭代器这种方法了。

HashSet类,底层数据结构存储方式为哈希表存储,该存储的特点是没有顺序,存储的时候根据给定的哈希算法,利用公式与法则来计算存储位置。并得出对处对象的hash值。

哈希值得计算使用的是hashCode()函数生成。

如果发生哈希值一样的情形,那么同哈希值的元素,在存储的时候会在同哈希值上发生罗列,即不通地址值对应同哈希值。该值可以通过。这点可以参考数据结构中,对于哈希表重叠问题的处理方法。同时,需要注意的是,这种叠加使用的是栈。如何验证HashSet类的无序性,我们可以通过先添加元素,再用迭代器依次输出,来验证顺序性。迭代器的next(),取的是地址顺序。

由此可推广,为什么Set是不能存储相同值的数据。对于HashSet来说,他的元素唯一性是由次HashSet类对象所添加的元素类的hashCode和元素equals两个方法类决定的。所以,一般都要覆写这两个方法。

如果对HashSet类对象做删除、查找操作,则先判断对应要查找的元素的哈希值,即使用hashCode方法,然后在比较内容是否相等,即调用equals方法。像这样的情况,犹如:对于contains()方法的调用,最先先比较哈希值,即调用hsahCode方法,然后再调用equals方法。同理,涉及元素比对的都是这样。

TreeSet类,可以对Set集合中的元素进行排序,默认的是自然排序,即ASCII表排序。而对于排序,我们一定涉及比较,当我们比较的对象为自定义类时,我们就需要实现Comparable接口,并覆写compareTo()方法。如此,才可以比较而不出现异常。这里有一点,当主条件相同时,比较次要条件是否相同。

class Personimplements Comparable
{
private String name;
private int age;
public int compareTo (Object obj)
{
if (!(instanceof Person))
throw newRuntimeException("非人");
Person p=(Person)obj;

if (this.age>p.age)
return 1;
if (this.age==p.age)
{
returnthis.name.compareTo(p.name);
}
return -1;
}
}


对于TreeSet类,一次一比显得比较麻烦,因此,该类的数据结构并不是线性的,而是一个LDR二叉树。建立的小根堆为ASC排序,大根堆为DESC排序。根据此二叉树的特性,如果保持原有序列,则,对应元素类中的compareTo方法的返回值取1即可,建立只有右子树的二叉树。如果想要倒序,则,对应元素类中的compareTo方法的返回值取-1即可,建立只有左子树的二叉树。这是元素自身具备比较性时,可以使用的方法。

当然,TreeSet类不止具有这一种比较方法。当元素自身不具有比较性,或者是比较性是已经定义好的不可更改,且并不是我们需要的时,我们采用的方法是让集合,即TreeSet类,本身具有比较性。这样,我们需要做的就是在该类对象初始化时,就直接为期制定比较方式。这样,我们需要参与的就是类的构造函数了。

TreeSet(Comparator<?
super E> comparator)

构造一个新的空TreeSet,它根据指定比较器进行排序。

这就是我们需要的构造函数。

Comparetor接口有两个方法声明:compare(o1, o2)和equals(obj)

compare(o1, o2):用于比较两个参数o1与o2是否相同;

equals(obj):用于判断对象obj是否等于次Cpmparator类对象。

需要强调的是,如果两种特性同时具备,以容器自身所携带的Comparator比较器为主,来排列顺序。

总的来说,TreeSet类的顺序,是由Comparator比较器和元素对象的compareTo方法来决定的。前者为主,后者为辅。

class Comp implementsComparator
{
public int compare (Object o1,Object o2)
{
if (!(o1 instanceof Person&& o2 instanceof Person))
{
throw newRuntimeException("人名错误");
}
Person p1=(Person)o1;
Person p2=(Person)o2;

/*
因为比较器使用时,是使o1在前o2在后的
所以我们想要和String自带比较方法相反
就得用p2来调用compareTo方法了
*/

return p2.name.compareTo(p1.name);

/*下面是一种复杂的写法,
当然我们也可以向上面的那样,简写
if(p1.name.compareTo(p2.name)==1)
return -1;
if(p1.name.compareTo(p2.name)==-1)
return 1;
return 0;
*/
}
}
/*
调用时如下:
TreeSet ts = newTreeSet (new Comp());
*/


------- android培训java培训、期待与您交流!
----------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: