java中级-2-Collection集合类知识串讲(1)-List及Set

集合
       Collection类接口所定义的集合是单列集合,较之Map类所定义的双列集合,他只有一个类型的可操纵元素。Collection接口实现了Iterable接口,该接口主要抽象了iterator()方法  。该接口有以下几个常见的声明:
       add(e):添加至末尾;     
       iterator():生成本对象的迭代器;    
       addAll(c):添加所有至调用对象的末尾; 
       remove(Object):从调用它的对象中,移除对应单个实例; 
       retainAll():取交集,存入调用它的一方   
       addAll():将参数方的所有元素都添加到调用方
       removeAll():移除调用一方中与参数相同的数据  
       containAll():检查调用方是否包含参数中的所有数据,真则反悔true,反之为false
       interator():调用迭代器,迭代器是集合取出元素的方式 Interator是一个接口,常用方法:
               hasNext():集合中是否还有元素;
               next():提取下一个   
               remove():移除迭代器指向元素  注意:next()每调用一次,就得用hasNext()判断一次,避免异常产生  Interator it=a1 . 
               interator():表明interator()函数返回值为Interator接口的一个子类对象,实现了多态。
       常用的Interator类具有局限性,并不提供添加操作。所以,我们对于复杂操作的迭代器使用根据需求,可选取ListIterator类来完成操作。较之Iterator类,ListIterator类多出了前驱判定、返回、回溯操作,以及对数据的增、改操作。但同时,ListIterator类也只能由List类调用。iterator方法是在接口类Iterable之中的,这是在jdk1.5后,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培训、期待与您交流! ----------


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值