黑马程序员->集合类 (1

来源:互联网 发布:linux查看内核版本 编辑:程序博客网 时间:2024/06/08 03:19

--------------------android培训、java培训、期待与您交流! --------------------

 

2......................集合类..............Collection..Collection...Collection
  //为什么出现集合类?
       面向对象对事物的体现都是以对象的形式,所以为了方便对
  多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式。

  //数组和集合类同是容器,有何不同?
    数组虽然也可以存储对象,但长度是固定的。集合长度是可变的。
   数组中可以存储基本数据类型,集合只能存储对象。

  //集合类的特点
    集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。
   Iterator                      Collection <-------------------------------Map
    |                                  /               \    Produces                       /   \  
   ListIterator            List               Set                       HashMap   TreeMap
                                    / |  \                |  \--------|    | 
                     ArrayList | Vector   HashSet         TreeSet   LinkedHashMap 
                LinkedList   |
           LinkedHashSet
   Comparable<-->Comparator
   //为什么会出现这么多的容器呢?
     因为每一个容器对数据的存储方式都不同,这个存储方式称之为:数据结构。
 2.1......ArrayList
       创建一个集合容器,使用Collection 接口的子类。 ArrayList
      1.添加元素     .add();
      2.获取个数,集合长度。.size();
      3.判断元素     .contains(x)/*是否存在x*/ .isEmpty(); //是否为空
      4.删除元素            .remove(x)/*删除x*/    .clear()//清空集合
         1.retainAll(2);//取交集,1中只保留和2中相同的元素。
         1.removeAll(2);//去掉1中和2中相同的元素。
   2.1.1...迭代器 Iterator
     迭代器就是集合取出元素的方式。 Iterator
     Iterator it = al.iterator();//获取迭代器,用于取出集合中的元素。
     while(it.hasNext()){// .hasNext();返回类型是boolean,所以用while循环,依次取
      sop(it.next());//出元素,直到取完,返回false,循环结束。
     }
     for(Iterator it = al.iterator(); it.hasNext(); ){
      sop(it.next());//一般用for循环来写,循环完it对象就清空,节省空间。
     }
    /* 取出方式定义在集合的内部,这样取出方式就可以直接访问集合内中的元素。
    取出方式就被定义成了内部类,而每一个容器的数据结构不同,所以取出的动作细节也不一样,
    但是都有共性内容:判断和取出。 那么就可以将共性抽取。
    那么这些内部类都符合一个规则。该规则就是Iterator,如何获取集合的取出对象呢?通过一个
    对外提供的方法:iterator(); */
  2.1.2.....List 集合
      Collection
       |----List ://元素是有序的,元素可以重复。因为集合体系有索引。
   线程不同步      |--ArrayList: 底层使用的是数组数据结构。特点:查询速度快,但是增删稍慢。
       |--LinkedList:底层使用的是链表数据结构。特点:增删速度很快,查询稍慢。
  线程是同步的  |--Vector:底层是数组数据结构。jdk 1.0 的老方法,过时了。
       |----Set ://元素是无序的,元素不可以重复。
   List :特有方法。凡是可以操作角标的方法都是该体系特有的方法
     增:.add(index,element);    addAll(index,Collection);//可以再指定位置加入元素。
     删:.remove(index); //可以删除指定位置的元素。
     改:.set(index,element);//修改指定位置的元素。
     查:.get(index);         subList(from,to);    ListIterator();//get可以通过角标获取元素。
 ListIterator :
       List 集合特有的迭代器, ListIterator 是 Iterator 的子接口。
       在迭代时,不可以通过集合对象的方法操作集合中的元素。
       因为会发生 ConcurrentModificationException 异常。

    /* 所以,在迭代时,只能用迭代器的方式修改元素,可是 Iterator 方法是有限的,
     只能对元素进行 判断,取出,删除 的操作。
     如果想要其他的操作如 添加,修改等,就需要使用其子类接口: ListIterator 。
     该接口只能通过 List 集合的 ListIterator 方法获取。*/
   .hasPrevious();//向上索引。
  Vector :
    枚举就是 Vector 特有的取出方式,发现枚举和迭代器很像。其实枚举和迭代是一样的。
    因为枚举的名称和方法都过长,所以被迭起取代了。 
    枚举和 Vector 是 JDK 1.0 版本的,被 迭代和 ArrayList 是 JDK 1.2 版本 取代了。
  LinkedList :
     链接列表数据结构。 特有方法:
     addFirst();      addLast();
     getFirst();   getLast();//获取但不删除,如集合中没元素,则出NoSuchElementException 异常。
     removeFirst();   removeLast();//获取并且删除元素,如集合中没元素,则出NoSuchElementException 异常。
   // 由于没元素时抛异常,在 JDK 1.6 版本中出现了替代方法:
     offerFirst();  offerLast();
     peekFirst();  peekLast();//获取但不删除,如集合中没元素,则返回 null;
     pollFirst();  pollLast();//获取并删除,如集合中没有元素,则返回null;

    
    如果list集合中,如 ArrayList ,要传入的集合是 new 出来的对象,如 Person 对象,而 Person 对象又有自己的
   name 和 age 属性。需求是把属性相同的 Person 对象视为同一对象,不予存入。
    那么,如果让集合自己去比较每个对象的话,是不能去除相同对象的,因为每个对象都是新 new 出来的,而集合默认比较
    的是地址值,所以每个对象都不一样。所以,就必须覆盖 默认的 equals 方法,重写 equals 方法成 比较每个对象
    的属性是否相同,这样才可以去除重复对象。
    //集合内 remove(去掉某个元素) 方法,默认比较对象时,也是调用 equals 方法,如果不复写equals方法,就会
    //默认认为对象都不相同,而无法 remove某个元素。


 2.2....Set 集合....//元素是无序的(存入和取出不一定一致),元素不可以重复。
   |---HashSet :底层数据结构是哈希表。线程是非同步的。
   |---TreeSet :
           Set 集合的功能和 Collection 是一致的。
      HashSet 是如何保证元素唯一性的呢?
        是通过元素的两个方法,hashCode和equals 来完成。
        如果元素的HashCode值相同,才会判断 equals是否为true。
        如果元素的hashCode值不同,不会调用equals。
      //对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashCode和equals方法。

     因为 HashSet 默认是先比较两个对象的 hashCode ,相同的话在去比较 euqals 。而每个对象的 hashCode 肯定
      不会相同(因为都是新 new 出来的),就一定要在对象内复写 hashCode 方法,可以写成每个对象内名字
      的 hashCode 方法(因为 String 对象已经复写了 Object 的 hashCode 方法,所以每个字符串的hashCode
      都不相同)和加上对象年龄乘以某正数(为避免有对象算出来的 hashCode 值偶然相同了)并 return 返回
      结果。然后还要复写掉 equals 方法(同 ArrayList 中复写基本相同)。


    |----TreeSet :可以对Set集合中的元素进行排序。  底层数据结构是二叉树。
      保证元素唯一性的依据:  compareTo 方法 return 0 ;

     TreeSet 排序的第一种方式:让元素自身具备比较性。元素需要实现 Comparable 接口,覆盖
       compareTo方法。这种方式也成为元素的自然顺序,或者叫做默认顺序。
     
     实现 Comparable 接口时,一定要复写抽象 compareTo 方法,在复写时,先判断一下传入的类是
     否为要比较的类对象,如果不是要抛出 RuntimeException 异常,因为是复写父类方法,所以不能声
     明异常,然后要把传入的 Object 对象强转回要比较的对象。然后用 if 语句判断。 return 数值。

   /* TreeSet支持两种排序方法:1.自然排序。2.定制排序。  默认情况下,TreeSet 采用自然排序。
     自然排序会调用元素的 compareTo 方法来比较元素之间的大小关系,然后将集合元素按升序
     排序,这就是自然排序。

     定制排序,自然排序是根据元素的大小,TreeSet 将它们以升序排序。如果要实现定制排序,如
     实现降序排序,就要重 new 对象并实现 Comparator(比较器),复写其中 compare 方法,对象
     上要关联该比较器。*/可用匿名内部类的形式来定制排序。

     //排序时,当主要条件相同时,要判断一下次要元素。

  2.2.2..二叉树       A元素
             /     \
           B(比A小)       C(比A大)
                      /     \        /        \
         D(比B小)   E(比B大) F(比C小)    G(比C大)
    //           最小的                           最大的
    TreeSet 的第二种排序方式。
      当元素自身不具备比较性时,或者具备的比较性不是所需要的。这时就需要让集合自身
      具备比较性。
      // 在集合初始化时,就有了比较方式。
     当两种排序都存在时,以比较器为主。 -------|
     定义一个类,实现 Comparator 接口,覆盖 compare 方法。

     实现 Comparator 接口时,要复写 compare 方法,先将传入对象强转回该对象,定义为两个对象
     的某个属性比较,调用属性的 compareTo 方法比较,并返回 return 数值。
2.3..泛型<>.............................................................................
   2.3.1.....集合内如存入不同的类型对象,则会出 ClassCastException 异常
          //类型转换异常,因为类型不同。
   所以:
     JDK 1.5 以后,出现新特性,用于解决安全问题,是一个安全机制。就是 <泛型>
   好处:
     1. 将运行时期出现的问题 ClassCastException ,转移到了编译时期。方便与程序员解决
     问题,让运行事情问题减少,安全。
     2. 避免了强制转换麻烦。
  泛型格式;
//               通过 < > 来定义要操作的引用数据类型。
   通常在集合框架中很常见,只要见到 < > 就是用来接收类型的。
   当使用集合时,将集合中要存储的数据类型作为参数传递到 <> 中即可。
//  什么时候定义泛型类??
   当类中要操作的引用数据类型不确定的时候,早期定义 Object 来完成扩展。
    现在定义泛型来完成扩展。
  //自定义泛型
   泛型类定义的类型,在整个类中有效,如果被方法使用,那么泛型类的对象明确要操作的具体
     类型后,所有要操作的类型就已经固定了。

    为了让不同的方法可以操作不同的类型,而且类型还不确定,那么可以将泛型定义在方法上。
    // 泛型定义在函数上,写在 返回值 的前面。
  //静态泛型定义
      静态方法不可以访问类上定义的泛型。如果静态方法操作的应用数据类型不确定,可以将
    泛型定义在方法上。 //否者要出---无法访问上下文异常。
     //泛型定义在接口上
 ...。泛型限定
     泛型可以设定 < > 内要传入的对象指定为某个类型。
   < ? >    ? :通配符,也可以理解为占位符。   
   <? extends E >   ://可以接受 E 类型或者 E 的子类型。---> 上限
   <? super E >   : 可以接受 E 类型或者 E 的父类型。---> 下限

--------------------android培训、java培训、期待与您交流! --------------------