黑马程序员_集合概述(Collection List&Set)

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ------- 

集合概述(Collection

第一部分 List & Itertor

一、集合概述

集合从功能理解上来说,其实和数组有相同之处,同样是存储数据,同样是容器,但又有所不同。我们知道,数组也是一个容器,在建立一个数组的时候,就规定了这个数组的长度以及里面要存的数据类型,当然数据也是可以存储对象的,只要类型是对象所属类型即可。但集合相对于数组来说,它的长度不是固定的,是可变的长度,它里面只能存储对象,就是说如果基本数据类型想要存入集合中,那么基本数据类型就得封装成基本数据类型的对象,集合可以存储不同类型的对象。

集合也分不同的容器,对这些容器的共性进行向上抽取,形成一个体系,这就是集合框架。当着集合框架产生,我们就可以看顶层的父类,看其中功能,就可以了解整个体系锁具有的基本功能,这样我们参考父类,创建子类对象即可。整个体系顶层,就成为集合Collection

Collection是一个接口,它有两个连个子接口ListSet,两个子接口下还有多个子接口:看图:

 

鉴于不同数据的存储方式都即数据结构都不同,所以集合框架中会有多个容器。

集合类的出现,是因为面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式。

 

数据和集合类同样是容器,但是又不同之处:

1、     数据虽然也可以存储对象,但是长度固定,集合长度是可变的

2、     数组中可以存储基本数据类型,集合只能存储对象

集合类特点,

1、     集合只能存储对象

2、     集合长度是可变的

3、     集合可以存储不同类型的对象

 

二、集合共性方法

首先建立一个List的子类对象,ArrayList为例。

首先建立ArrayList对象,这样就建立了一个容器

                   ArrayList  a = new ArrayList()

1、     添加元素,addObject obj

    al.add("java01");

al.add("java02");

add方法接收的是Object类型,以便于接受任意类型的对象。

集合与数组一样时容器,同时一样的是存储方式,存储的都是对象的引用地址值。

2、     获取长度

int  size  =a.size() 返回的是int

3、删除元素

            a.remove("java01" );删除一个对象

         a.clear();  清空集合

4、判断元素

         boolean a.contains"java01")判断是否包含某个元素

a.          isEmpty()判断集合是否为空

4、     改变集合元素

集合1.retainAll (集合2):即集合1中只会保留与集合2中相同的元素。

集合1.removeAll (集合2):即集合1中会删除与集合2中相同的元素。

 

三、元素取出:迭代器

         集合中的迭代器,其实就是集合的去除元素的方式。示例程序如下:

                   ArrayList al= new ArrayList();

                   al.add("java01");

                   al.add("java02");

                   Iterator it = al.iterator();

                   while(it.hasNext()){

                            System.out.println(it.next());

                   }

集合al继承了父类的iterator方法,会放回一个iterator的子类对象,其中有

方法hasNext(),这是判断集合中是否有月元素,有的话返回true,没有的话返回false

方法next(),这就逐个去除集合中的元素,返回迭代的下一个元素。

 

不同的容器,元素的取出方法就会不同,有几个容器,可能就会有几个方法,这些方法可能会有共同之处,比如都有取出动作,当然每个取出动作都不一样,但有共性内容。我们可以将这这取出动作,进行抽象的抽取,描述成一个新接口Iterator。这样,把取出方式定义在集合内部,这样的取出方式就可以直接访问集合中的元素,那么取出的具体方式就定义成了内部类。有共同的规则即接口,有具体实现的内部类,那么集合就通过对外提供的一个方法iterator(),让我们能够取出集合中的元素。

 

四、List

Collection中包含两个体系,上图可知:

1、  List 元素是有序的,元素可以重复,因为集合体系有索引。相当于数组。

2、  Set 元素中是无序的,元素不可以重复。

 

List集合的特有方法:
1、添加元素

  add(“java 00”);

2、在指定位置添加元素

   add( 1,”java 01”);

3、删除指定位置元素

   remove ( 2 )

4、修改元素

   set( 4 , “java ok”);

5、通过角标获取元素

   get( 3);

6、获取对象位置

   indexOf ( “java 01”);

7、获取子集合

   subList ( 3,4 )  包含头不包含尾。

 

关于ListIterator

ListIteratorList集合的特有方法,关于怎么用先看一下代码:

           ArrayList al = new ArrayList();

           al.add("java01");

           al.add("java02");

           al.add("java03");

 

           Iterator it = al.iterator();

           while(it.hasNext()){

                    Object obj = it.next();

                    if(obj.equals("java02"))

                             al.add("java009");

}

上面代码中,我建立一个List集合的对象,然后进行迭代器迭代,在迭代的过程中,对集合中元素进行操作,但是这时系统汇报一个异常ConcurrentModificationException,意思就是并发操作非法。简单来说,我们建立了一个List集合,这个集合对象可以操作其中的元素,同时,我们通过这个对象得到一迭代器Iterator,这个迭代器同时也可以操作这个集合中的元素,这样在迭代过程中,我们遍历元素用的是Iterator,增加元素用的是集合对象al。此时,两个对象操作同一个集合的元素,就会产生安全隐患,比如,集合对象减掉一个元素,而迭代器确认为这个元素是存在的,这样就冲突了。

这时,我们想只用迭代器进行增删改查,但是迭代器只有三个方法,hasNextnextremove,这三个方法不能满足我们所需要的。所以就用到了ListIterator这时一个Iterator的子接口,这个子接口中有对元素的判断、取出、删除、添加、修改等操作,这时子接口就很实用了,满足我们的要求。

通过List中方法listIterator,获取这个接口的对象。具体程序如下:

                  ArrayListal = new ArrayList();

                  al.add("java01");

                  al.add("java02");

                  al.add("java03");

                  al.add("java04");

 

                  Listiterator it = al.listIterator();

                  while(it.hasNext()){

                           Objectobj = it.next();

                           if(obj.equals("java02"))

                                    it.add("java009");

 

方法时这样的,注意的是ListIterator中,hasNext(正向遍历)和hasPrevious(逆向遍历)两个方法中指针的走向。不管两个方法哪个先用,指针都是在0角标为,这就意味着hasNext此时返回值为true,就是说指针后面有元素;而此时,hasPrevoius返回为false,就是说指针前面没有元素。只有当使用hasNext后,指针后移,然后再使用hasPrevious返回的结果才为true。当hasNext遍历完后,在使用hasPrevious就会逆向遍历了。

 

 

 

List集合具体对象的特点:

有一中的关系如可知,List包含以下三个具体的对象,各自有特点:

1、 ArrayList

底层数据结构是数组结构。特点是:查询更改很快,增删稍慢。线程不同步。

初始长度为10,需要改变的话,每次增加50%

2、 LinkedList

底层数据结构是链表结构(就是A-B-C-D-F-G-H,知道前者才知道后者)。特点是,增删很快,查询稍慢。线程不同步。

3、 Vector

底层数据结构是数组结构,线程同步。早于ArrayList,西安被ArrayList替代了。无论增删或查询都很慢。

初始长度为10,需要改变的话,每次增加100%

 

1)关于Vector

Vector中,凡是带elements都是它的特有方法。

他有一个特别的方法,elements(),它会返回一个Enumeration(枚举)的子类对象。

枚举就是Vector特有的取出方式,其实枚举和迭代器很像,但是因为枚举的名称和方法名都过长,所以逐渐被枚举取代了。

 

2)关于LinkedList

LinkedList特有方法:

addFirst();在链表头部加入一个元素

addLast();在链表尾部加入一个元素

 

getFirst();获取头部元素,但不删除

getLast();获取尾部元素,但不删除

获取元素,但不删除元素,如果集合中没有元素,会有NoSuchElementException

removeFirst();移除头部元素,同时删除元素

remoceLast();移除尾部元素,同时删除元素

获取元素,但是删除元素,如果集合中没有元素,会有NoSuchElementException

 

JDK1.6中出现的替代的方法

offerFirst();

offerLast();

 

peekFirst();

peekLast();

获取元素,但是不删除元素,如果集合中没有元素,返回null

pollFirst();

pollLast();

获取元素,但是元素被删除,如果集合中没有元素,返回null

 

List编程时应注意的:

         List集合判断元素是否相同,依据的是元素自身所有的equals方法,

判断元素,containsremove方法在使用时,就是依据equals。比如contains,在判断是否包含的时候,会在底层调用元素自身所有的equals方法进行判断比较,而元素尤其是自定义元素都是继承自Object中的equals,比较的是对象的地址值,所以,即便是两个元素内容相同,也会判断为不同,因为地址值就不一样。所以,我们在自定义元素的时候,一定要复写equals方法。看一个自定义元素的内部方法:

class Person{

-----------------------省略部分代码---------------------

         public boolean equals(Object obj){

                   if(!(objinstanceof Person))

                            returnfalse;

                    Person p = (Person)obj;

                    return this.name.equals(p.name) &&this.age==p.age;

         }

}

注意equlas方法的复写

 

 

 

      第二部分 Set

Collection又一子集合

一、            HashSet

Set集合,元素是无序的(存入和取出的顺序不一定一致),没有重复元素。Set集合的功能和Collection是一致的。

         HashSetSet下的子集合,其功能和List中的功能基本一致。HashSet:底层数据结构是哈希表。

         了解HashSet,先要了解HashSet的内存机制。因为其元素是无序的,且没有重复元素。它在内存中这样存入元素的:

首先会判断每个元素的哈希值,如果哈希值不同,那么它就会根据哈希表,把地址值放入相应的位置上,这样即使先存入的元素可能也不会在前面,所以其取出和存入的顺序不一定一致。如果哈希值相同,那么它就会判断元素的内容是否相同,如果相同,那么就不会存入集合中,如果不同,就会在这个相同的哈希值下面顺延,存入这个元素。这样就保证了了元素的唯一性。

简单来说,HashSet如何保证元素的唯一性:

        是通过元素的两个方法,hashCodeequals来完成。如果元素的hashCode值相同,才会判断equals是否为true。如果hashCode不相同,不会调用equals

 

其实在思想上来说,和List中,我们想要去除相同元素的想法是一样的。在HashSet中,我们想要定义自己的元素,就要复写hashCodeequals方法,这样才会执行上面描述的判断。示例程序如下:

classPerson{

------------------省略部分代码---------------

public int hashCode (){

         retrun  name.hashCode( ) + age*11

}

         publicboolean equals(Object obj){

                   if(!(obj instanceof Person))

                            return false;

                    Person p = (Person)obj;

                    return this.name.equals(p.name) &&this.age==p.age;

         }

}

比较ListHashSet

不同点:前者是在判断是否相同元素时,会用equals方法;后者是在添加的时候,就动用hashCodeequals方法,而且在判断元素是否相同时,先会调用hashCode方法,不同同则不调用equals方法,相同就调用equals方法。

相同点:都比较依赖元素自身的方法

 

二、TreeSet

TreeSet可以对元素进行自然排序。

当我们在给TreeSet中添加元素的时,会按照自然顺序进行排序。但如果我们存入自定义的元素,就会失败。这里就要解释下为什么:很简单,Tree要求存入的元素具备比较性,怎么样具备比较性,其实就是实现Comparable,而Comparable中只有一个方法,就是compareToObject obj),那么就会用到元素自身所有的compareTo方法了。注意返回值为int,大于就是正数,小于就是负数,等于就是零。

            我们在自定义元素的时候,只要实现Comparable接口,实现其compareTo方法即可,在方法中定义判断条件。看示例程序:

class Person implements Comparable{

-------------------------省略部分代码---------------

            publicint compareTo(Object obj){

                   if (obj instanceof Person)

                            throw newRuntimeException("class is wrong");

                   Person p = (Person)obj;

                   if(this.age>p.age)

                            return 1;

                   if(this.age==p.age)

                            return this.name.compareTo(p.name);

                   else

                            return -1;

            }

上面就是具体的方法示例,但是要注意一点,当我们自定义元素的时候,可能会有多个判断条件,就是说,在TreeSet集合中,想要按我们自己要求的顺序打印,就要分清主次条件,当主要条件相同时,一定要判断次要条件

 

在返回最初,我们想要了解的底层数据结构。

TreeSet底层数据结构,是一种二叉树结构。语言描述比较繁琐,直接上图:

 

已上图来描述,当最先存进去的是3,这是在二叉树顶部,然后存进2,它会先跟3比较,比3小,那么就放到3的左边。再存入1,又先跟3比,比3小,再跟3的左边比,也比2小,那么就放到2的左边。再存入7,先跟3比,比3大,放到3的右边。再存入5,先跟3比,比3大,再跟7比,比7小,放到7的左边。再存入8,比37都打,放到7的右边。一次类推。这就是二叉树结构。

当我们程序中进行判断时,TreeSet集合主要是看元素中,compareTo返回的值是正数、负数还是零。TreeSet集合取元素的时候,默认是从最左边开始取。因为这样,我们就可以控制二叉树的排序,比如我们把元素compareTo的返回值固定为1,那么元素就会向左排序,那么取出的时候就有了输入时候的顺序。

 

TreeSet排序的第一种方式:让元素自身具备比较性,元素需要实现Comparable接口,覆盖compareTo方法,这种方式成为元素的自然排序,或者叫做默认排序。

 

TreeSet排序的第二种方式:当自身元素不具备比较性时,或者具备的比较性不是所需要的,这时就让集合自身具备比较性。方式就是,定义一个比较器compartor,将比较器对象作为参数传递给TreeSet集合的构造函数。那我们就自定义一个比较器,实现Comparator,复写其中的compare方法,然后把比较器对象传入TreeSet集合的构造函数即可。

实现程序如下:

class MyCopare implementsComparator{

         public  int  compare(Object o1,Object o2){

                   Person p1 =(Perosn)o1;

                   Person p2 =(Perosn)o2;

                   int num =p1.getName().compareTo(p2.getName());

                   if (num==0){

                            returnnew Integer(p1.getAge()).compareTo(new Integer(p2.getAge()));

                   }

                   return num;

         }

}

既然TreeSet有两种排序方式,如果当两种排序方式都存在是,以比较器为主。

 

 

 

 

 

 

 

 

Collection总结:

1、           Collection是一个容器,它的特点是,只能存储对象,对象类型可变,长度可变。而数组可以存储对象,也可以存储基本数据类型,但只能存一种类型,长度固定。

2、           Collection作为父类,看其中的方法,建立子类对象即可使用其中的方法,那么这里就有共有的方法在整个Collection都可使用,进行增删改查判断的操作。

3、           我们要操作集合中元素,尤其是遍历元素,就用到迭代器Iterator,获取方法时当前集合的iterator方法,即可获得迭代器。使用迭代器中的hasNextnext即可遍历集合。值得注意的是,想在遍历过程中操作集合,那么在List集合中就用listIterator方法返回一个ListIterator对象,使用这个对象中的方法即可(这个对象中的方法和集合共性方法相似)。同时理解迭代器原理,其实是每个集合的内部类,Iterator是引用。

4、           Collection下有两个大的子类,分别为ListSet

List:有序集合,元素可以重复,有索引。

Set:无序集合,元素不可以重复

5、           List中子集合:

ArrayList

底层数据结构是数组结构。特点是:查询更改很快,增删稍慢。线程不同步。

初始长度为10,需要改变的话,每次增加50%

   LinkedList

底层数据结构是链表结构(就是A-B-C-D-F-G-H,知道前者才知道后者)。特点是,增删很快,查询稍慢。线程不同步。

Vector

底层数据结构是数组结构,线程同步。早于ArrayList,西安被ArrayList替代了。无论增删或查询都很慢。

初始长度为10,需要改变的话,每次增加100%

 

重点是:List集合在进行元素的判断的时候,要复写equals方法。

6、           Set中的子集合:

HashSet:底层数据结构是哈希表。HashSet中元素无序,是因为它是根据元素地址值来存入内存中的,而地址值就是哈希值,每个元素的哈希值不一样,所以存入顺序和输出顺序会不一致。

为了保证集合元素的唯一性,再加入元素时,HashSet会先判断hashCode的值是否相同,如果不同则存入,如果相同在equals方法判断,不同存入、相同排除。所以如果我们自定义元素,最好复写hashCodeequals方法。

 

TreeSet:底层数据结构是一种二叉树结构。

为了保证元素的唯一性,TreeSet集合也会对元素进行比较,按自然顺序排序。但这里要求元素具有可比较性,就里就涉及到两种排序方式。

一、让元素自身具备比较性,元素需要实现Comparable接口,覆盖compareTo方法,这种方式成为元素的自然排序,或者叫做默认排序。

二、当自身元素不具备比较性时,或者具备的比较性不是所需要的,这时就让集合自身具备比较性。方式就是,定义一个比较器compartor,将比较器对象作为参数传递给TreeSet集合的构造函数。那我们就自定义一个比较器,实现Comparator,复写其中的compare方法,然后把比较器对象传入TreeSet集合的构造函数即可。

注意compareTocompare方法返回的都是int型值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值