41.黑马程序员-集合框架、泛型

来源:互联网 发布:淘宝账号出售 编辑:程序博客网 时间:2024/06/02 05:30

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

 一、集合框架概述    
    1.体系概述
  • 为什么出现集合类?
    • 面向对象语言对事物的体现都是以对象的形式,对象多了,出现了存储对象的集合。
    • 数据多了,需要进行封装成对象。有两种存储方法:数组和集合。
  • 有数组为什么出现集合?
    • 对象多了用集合存储,数据多了用对象存。
    • 数组是固定长度的,集合是可变长度的。
    • 数组只能存同一种数据,集合中只要是任意对象就可以。
    2集合的特点
  • 集合作为容器可以分为很多种,如水杯就有很多种。
    • 多种集合的共性抽取,就是集合框架:Collection。 抽取的不一定能创建对象,所以需要参阅顶层创建子类对象。
    • 集合中元素取出方式:迭代器、遍历、for循环、按角标索引 、枚举
    3.集合框架
  • Collection
    • |--List //元素是有序的,元素可以重复,因为该集合有序。
      • |--ArrayList
      • |--LinkedList
      • |--Vector
    • |--Set//元素是无序的,
      • |--HashSet
      • |--TreeSet
  • 为什么出现这么多的容器?
    • 因为每一种容器的存储方式都不同,这个存储方式称之为数据结构。
  • 集合共性方法:
    • 1.add方法: add方法的参数类型是Object,以便于接受任意类型对象。
    • 2.集合中存储的都是对象中的引用(地址)。
      • 栈里存放集合和数据对象的引用,堆中存放集合对象(包含数据对象的引用)、数据对象。
    • 3.删除元素。
      • remove("java02");//删除java02这个元素。
      • clear(); //清空
    • 4.判断元素
      • al.contains("java02");//java02是否存在。
      • al.isEmpty();//al是否为空。
    • 5.取交集retainAll
      • al1.tetainAll(al2);//取交集,没交集为空。
二、迭代器
    1.概念
  • 迭代器就是集合的取出元素的方式。
  • 迭代器 Iterator是一个接口。
  • 接口型引用只能指向自己的子类对象。 
    • 接口引用指向了子类对象,意思就是接口的引用能够指向实现它的类的一个对象,然后通过这个接口的引用,来调用接口的属性和方法(调用的其实是实现了他的类的方法)。
  • 取出方式、Iterator接口、内部类
    • 取出比较复杂,不足以用一个方法描述,需要多个功能,将取出动作封装成一个对象。
    • 取出对象描述成一个类,因为只操作集合中的元素定义在内部比较方便,所以此类为内部类。完成取出动作的定义。
    • 因为各种集合数据结构不同,各有自己的取出对象。但是都有共性内容:判断、取出。共性可以抽取为Iterator接口。






  • 定义Iterator iterator ();
  • 使用方法一:
    • 第一步:ArrayList al = new ArrayList();
    • 第二步:Iterator it = al.itarator();//
      • itarator对象是通过集合的方法获取出来的,不是new出的,
      • 然后接口型引用it指向了al.itarator这个对象,这样可以调用al.itarator的方法。
    • 迭代器可以获取集合中的元素。获取方法:
    • 第三步:while(it.hasNext())//这里 it.hasNext()当有元素返回true。
      • {
        • sop(it.next());//每次输出下一个元素。
      • }
  • 使用方法二
    • for(Iterator it = al.iterator(); it.hasNext;        )//好处是it用完释放,节省内存。
    • {
      • sop(it.next());
    • }
    三、List集合
  • ListIterator :
    • ListIterator概念
      • Iterator当添加完元素并且建立完迭代器后, 因为迭代器只知道前面的元素 ,不要再使用集合方法添加、删除元素,会引发并发修改异常。
      • Iterator迭代器有局限性,只能删除,不能添加。为了解决Iterator局限性,可以使用它的子接口ListIterator。
      • 因为 ListIterator迭代器有指针、角标,所以方法比Iterator多的多,可以添加、判断、删除、取出、修改等。
      • 只有List集合有在遍历过程中增删改查的方法,因为它有角标。
    • ListIterator使用方法
      • 增删改查。
      • boolean hasPrevious():
        • 如果以逆向遍历列表,列表迭代器有多个元素,则返回true。
        • 意思与hasNext判断后面有没有相反,是判断前面有没有。
  • ArrayList
    • ArrayList概念
      • 底层的数据结构使用的是数组结构。
      • 线程不同步。
      • 可变长度数组:默认长度为10的空列表。超过则延长50%。将原来数组copy到新数组来,再把新元素添加到新数组中。
      • 数组结构是每个元素都有编号。
      • 特点是查找很快、修改很快。 增删需要移位 ,增加、删除较慢。元素越多越明显。
  • LinkedList
    • LinkedList概念
      • 底层使用的链表数据结构。
      • 线程不同步。
      • 链表是后面一个元素记住前一个链表。
      • 特点是增删速度很快、查询、稍慢、修改需要依次往下问,增删的时候只需要这个元素记住前后元素就可以。
  • Vector
    • Vector概念
      • 底层是数组数据结构,JDK1.0出现。 被ArrayList替代。
      • Vector是同步的。
      • 可变长度数组:默认长度为10的空列表。超过则延长100%。
    • Vector
      • 枚举是Vector的特有取出方式。需要枚举时使用Vector。
      • 迭代器与枚举的区别:迭代器是枚举的功能加强,迭代器添加了一个可选的移除操作,并使用较短的方法名。优先使用迭代器。
    四、Set集合
  • |--Set
    • |--HashSet
      • 数据结构是哈希表,线程是非同步的。
      • 保证元素唯一性的原理:hashCode值是否相同。如果相同,判断equals方法。
    • |--TreeSet:不常见。
      • 可以对Set集合中的元素进行排序。
      • 底层数据结构是二叉树。
      • 保证元素唯一性的原理:compareTo方法return 0.
      • TreeSet排序的第一种方式:让元素自身具备比较性。元素需要实现Compareble接口,实现compareTo方法。这种默认排序方式被称为自然顺序
      • TreeSet排序的第二种方式:
        • 当元素不具备比较性,或者比较性不是所需要的,这时就需要让集合具备比较性。
        • 集合相当于刻度板,两个元素在刻度板下一站就能比较出来。
        • 定义一个类实现Comparetor接口,覆盖compare方法。 在集合初始化时,就有了这种比较方式,把这个类作为参数传给TreeSet的构造方法。
        • 保证唯一性:return 0判断唯一性。
    • Set :元素是无序的(存入和取出顺序不一定一致),并且元素不可以重复。
    • Set<E>是一个接口。
    • Set集合的功能和collection功能是一致的。
  • HashSet集合
    • 底层结构是Hash表
    • Hash表:
      • 存放一堆Hash值,存的顺序是按照Hash值,取得时候顺序不一定一致,
      • 如果Hash值重复的时候,equals判断元素是否是一个对象,不是一个对象会在另一个对象下顺延
      • HashSet方法类似于Collection,取出元素用Iteratoe迭代器。
      • HashSet的对象比较需要判断:重写了的hashCode()和equals(), hashCode()判断相同时,再判断equals().为了效率,hashCode()中就直接判断对象是否相同,不同直接return 负数。相同再equals()。
      • 想要HashSet中的元素对象唯一,需要对象 equals挨个判断是否相同(默认equals判断地址,需要复写)。但是前提是HashCode相同才会判断equals,所以首先复写HashCode,然后复写equals。*39
  • 二叉树-个人理解:
    • 二叉树存储:
      • 第一个值定位初始值,以后的值与这个初始值比较,分成两个叉:左小右大,依次一层层向下排。
      • 值多了以后,初始值就不一定是中间值效率就低了,这时二叉树会自动取折中值。
    • 二叉树取出:
      • 从小打到取,从左叉的左下角最小值开始依次取到顶
      • 再到右叉取,从右叉的左下角最小值开始依次取,每一次取到上一层再取上一层的右小叉,一直到结束。
    • 二叉树的小技巧
      • Set中,元素一般是无序的,但是TreeSet中使用一个小技巧可以实现怎么存的怎么取出:compareTo()方法返回正数即可。当然也可以返回负数取反,返回0只取第一个。
   五、Map接口
   1.概述
接口Map<K,V>  
K:key。V:value。
作用:将键映射到值的对象,一个键只能映射一个值,键是唯一的。
区别:双列集合。
    2.功能
添加:注意,如果添加相同键,后值覆盖前值,并put方法返回被覆盖的值
删除
判断
 
获取
    3.Map扩展
Map集合被使用是因为具备映射关系。
Map是键-值对形式,一般是两个元素映射,如果有三个元素呢?
解决方法是一个键对应一个值(键值对),实现一对多的关系。
格式如下 HashMap <键的类型, HashMap  <键的类型 ,值得类型>> 引用变量= new HashMap <键的类型, HashMap  <键的类型 ,值得类型>>();
举例:HashMap<String,HashMap<String,String>> hm = new HashMap<String,HashMap<String,String>>( );
put();
map.put(01,"zhangsang");// 第一次存 ,这一句的返回值是null,
map.put(01,"lisi");//这一句的返回值是zhangsan。
为什么会这样,put()返回的是这个键原来的值,形象点就是,压入一个值把原来的值挤出来了^^。
     4.KeySet
map集合的两种取出方式:
1,返回set<k>        
keySet():将map中所有的键存入到Set集合。因为set具备迭代器。所有可以迭代方式取出所有的键,在根据get方法。获取每一个键对应的值。
举例:
2,返回set<Map.Entry<K,V>>
entrySet():将map集合中的映射关系取出,存入到Set集合中。
举例:
    5.Map中需要注意的:
当一个map存放多个对象时,就需要让对象具备比较性,有备无患。
创建的对象中也需要注意:
1,为了比较对象,覆盖hashCode()和equals()。覆盖hashCode的原因是jvm调用equals前会先调用hashCode()。
2,再就是需要implements实现comparator接口,覆盖compareTo()以便比较.
                                      六、泛型
                                    • 泛型是JDK1.5以后的新特性,用于解决安全问题,是一个安全机制。
                                    • 好处:
                                      • 1,将运行时期出现的问题ClassCastException,转移到了编译时期。方便于程序员解决问题,让运行时期问题减少,安全增加。
                                      • 2,避免了强制转换的麻烦。
                                    • 格式:通过<>来定义要操作的引用数据类型。
                                    • 用途:什么时候使用泛型。
                                      • 通常在集合框架中很常见,只要见到<>就要定义泛型,例如接口Collection<E>,ArrayList<E>.
                                      • 其实<>就是接受类型的,当使用集合时,将集合中要存储的数据类型作为参数放到<>中即可。
                                      • 重点:泛型类、泛型方法。
                                        七、泛型类
                                    • 什么是泛型类,就是带泛型的类。
                                    • 定义位置:
                                      • 类名后面: class Utils<泛型>,Demo <泛型> d = new Demo<泛型>();
                                      • 方法返回值前面:public <T> T findById(int id)
                                    • class 类型 <泛型>
                                    • 什么时候定义泛型类?
                                      • 当类中要操作的引用数据类型(基本数据类型不行)不确定的时候,早期定义Object来完成扩展,现在定义泛型来完成扩展。
                                    • 举例
                                    • //泛型类,就是带泛型的类--!class Utils<QQ>{private QQ q;public void setObject(QQ q){this.q = q;}public QQ getObject(){return q;}}



                                     
                                    • 使用泛型类
                                    Utils<Worker> u = new Utils<Worker>(); //new 一个泛型类对象。

                                    u.setObject(new Worker()); //new一个对象作为参数传入。
                                    Worker w = (Worker) u.getObject(); //获取对象。注意这里Object转为Worker
                                        八、泛型方法
                                    • 把泛型定义在方法上,就是泛型方法。
                                    • 泛型定义位置:返回值前面
                                    • public static <W> void method(W w)
                                    • 作用:
                                      • 泛型定义在类上,会对整个类有效,泛型类中的方法类型也就确定了。
                                      • 为了让不同方法可以操作不同类型,而且类型还不确定。
                                    • 举例:
                                    • class Demo{public<T> void show(T t){System.out.println("show:"+t);}public <Q>void print(Q q){System.out.println("print:"+t);}}
                                      • 这时候可以多个相同或不同方法可以使用不同的泛型。
                                      • d.show("haha");
                                      • d.show(new Integer(4));
                                      • d.print("haha");
                                      • d.print(new Integer(4));
                                      • 这个泛型仅仅作用于这个方法,传什么操作说明,新方法用另外的泛型。
                                    • 泛型类和泛型方法可以同时使用
                                    • class Demo <T>{public <T>void show(T t){System.out.println("show:"+t);}public <Q>void print(Q q){System.out.println("print:"+t);}}


                                        九、静态方法泛型
                                    • 静态方法不可以访问类上定义的泛型,原因是静态方法存在的时候对象还不存在。
                                    • 可以将泛型定义在静态方法上。
                                    • class Demo <T>{public static <M> void method(M m){System.out.println("method:"+m);}}

                                        十、泛型接口
                                    • 泛型定义在接口上。
                                    • interface Inter<T>{void show(T t);}class InterImpl <T> implements Inter<T>{public void show(T t){System.out.println("show:"+t);}}

                                        十一、泛型限定
                                    • 泛型定义在参数上,如
                                      • public static void printColl(ArrayList<String> al ){}
                                      • 可以写成这样
                                        • public static void printColl(ArrayList<?> al ){}
                                        • 好处是?限定符可以是任意类型。
                                        • public static void printColl(ArrayList<?> al1 ){Iterator <?> it = al.iterator();while (it.hasNext()){System.out.println(it.next());}}


                                        • ?与T的区别,?代表不确定的类型后面不可用,T在后面可以作为确定的类型使用,比如T T = it.next();。
                                        • 那么?不如T的话,好处是什么?
                                          • 子类继承了父类,而在子类存入父类类型集合中,却不可以。
                                          • ?可以限定传入的参数类型。 
                                          • public static void printColl(ArrayList<? extends Person> al1 )。只能传入Person及其子类。迭代器中泛型也要 <? extends Person>

                                    原创粉丝点击