06-java学习笔记-集合框架

时间:2023-02-25 18:04:22
1.集合框架概述

集合类

为什么出现类集合?
     面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,
     就对对象进行存储,集合就是存储对象最常用的一 种方式。
数组和集合类同是容器,有何不同?
     数组虽然也可以存储对象,但长度是固定的;集合 长度是可变的。
     数组中可以存储基本数据类型, 集合只能存储对象
集合类的 特点
     集合只用于存储对象,
     集合长度是可变的,
     集合可以存储 不同类型的对象。
2.集合框架的构成及分类&共性功能
06-java学习笔记-集合框架
注:虚线框都是接口
集合体系的共性内容都在Collection
Collection 是一个接口
Collection中的常见方法:
1.添加
      boolean add(Object obj);
      boolean addAll(Collection coll);
2.删除
      boolean remove(Object obj);
      boolean removeAll(Collection coll); // 删除集合中所有的相同元素
3.判断
      boolean contains(Object obj);
      boolean containsAll(Collection coll); // 集合中是否包含另一个集合的所有元素
      boolean isEmpty(); // 判断集合中是否有元素
4.获取
     int size();
     Iterator iterator();//取出元素的方式:迭代器
     该对象必须依赖于具体容器,因为每一个容器的数据的数据结构不同
     所以该迭代器对象是在容器中进行内部实现的
     所以对使用容器者而言,具体的实现不重要,只要通过容器获取到该实现的迭代器对象即可
     也就是iterator方法
     Iterator接口就是对所有的Collection容器进行元素取出的公共接口
5.其他
     boolean reatainAll(Collection coll); //取交集 , 如果有交集返回true
     Object[] toArray();                         //将集合转成数组
3 迭代器的使用
     迭代是取出集合中元素的一种方式。
     因为Collection中有iterator方法,所以每一个子类集合对象都具备迭代器。
方法:
     1. boolean hasNexrt() //如果仍有元素可以迭代,则返回 true。
     2. E next()                   //返回迭代的下一个元素。
     3. void remove()         //从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。
  
用法:
Collection coll = new ArrayList();
// 方式1
Iterator it = l.iterator();
while(iter.hasNext())
{
     System.out.println(it.next());
}
// 方式2 结束后it 不可用 
for( Iterator it = iterator(); it.hasNext(); )
{
     System.out.println(it.next());
}
通过内部类实现
迭代注意事项
迭代器在Collcection接口中是通用的,它替代了Vector类中的Enumeration(枚举)。
迭代器的next方法是自动向下取元素,要避免出现NoSuchElementException。
迭代器的next方法返回值类型是Object,所以要记得类型转换。
思考:为什么next方法的返回类型是Object的呢?
因为集合中的元素类型是Object
4 集合框架的两个常用接口List和Set的特点
集合框架的常用接口
Collection接口有两个子接口
List(列表),   Set(集)
List: 可存放 重复元素,元素存取是 有序
Set: 不可以存放重复元素,元素存取是 无序的。
5 List
List接口中常用类
     Vector:线程安全,但速度慢,已被ArrayList替代。
     ArrayList:线程不安全, 查询速度快。
     LinkedList:链表结构, 增删速度快。
List特有的常见方法:有一个共性的特点 就是都可以操作角标。
取出LIst集合中元素的方式:
     1.     get(int index):通过脚标获取元素。
     2.     iterator():通过迭代方法获取迭代器对象。
1. 添加
          void add(index, element);
          void add(index, collection);
2. 删除
          Object remove(index);
3. 修改
          Object set(index, element);
4. 获取
          Objext get(index);
          int indexOf(object);
          int lastIndexOf(object);
          List subList(from, to);
List集合是可以完成增删改查的。
ListIterator接口

     iterator的操作再用集合的操作就会产生异常ConcurrentModificationException
     所以List 接口提供了特殊的迭代器,称为 ListIterator,
     除了允许 Iterator 接口提供的正常操作外,
     该迭代器还允许元素插入和替换,以及双向访问。
     还提供了一个方法来获取从列表中指定位置开始的列表迭代器。
     通过List中的 ListIterator<E> listIterator()方法获取。
                  或者ListIterator<E> listIterator(int index)
     ListIterator 接口中的方法:
     还可以双向遍历 hasPrevious()     previous() 方法
     1. void  add( E e) 添加
     2. void remove()
     3. void set(E e)
     
6 List常用子类

List:
     |--Vector        内部是数组数据结构,是同步的。增删,查询的速度都很慢
     |--ArrayList     内部是数组数据结构,是不同步的。替代Vector。查询的速度快
     |--LinkedList   内部是链表数据结构,是不同步的。增删元素的速度很快

7 Set常用子类
Set接口中常用的类

Set:无序,不可以有重复元素。
     |--HashSet     线程不安全
                           数据结构是哈希表。线程是非同步的
                           保证元素的唯一性的原理:判断元素的hashCode值是否相同。
                           如果相同,还会继续判断元素的equals方法,是否为true。
     |--TreeSet       线程不安全
                           可以对Set集合中的元素进行排序。
                           底层数据结构是二叉树
                           保证元素唯一性的依据是compareTo方法return 0;
                           TreeSet排序的
                           第一种方式:让元素自身具备比较性
                                   元素需要实现Comparable接口,覆盖compareTo方法
                                   这种方法也称为元素的自然顺序,或者叫做默认顺序
                           第二种方式
                                   当元素自身不具备比较性时,或者具备的比较性不是所需要的
                                   这时就需要让集合自身具备比较性。
                                   在集合初始化时,就有了比较方式。构造函数
HashSet:线程不安全,存取速度快。
TreeSet:线程不安全,可以对Set集合中的元素进行排序。
它的排序是如何进行的呢?
     需要使其中的元素具备比较性
     即实现Comparable接口的int compareTo(T o)方法
Set集合元素唯一性原因
     HashSet:通过equals方法和hashCode 方法来保证元素的唯一性。
     TreeSet:通过compareTo或者compare 方法中的来保证元素的唯一性。元素是以二叉树的形式存放的。
存储自定义对象
TreeSet ts = new TreeSet();
ts.add(new Student("lisi", 20);
ts.add(new Student("zhangsan", 21);
出现类型转换异常 ClassCastException    
排序时,当主要条件相同时,一定要比较次要条件。
class Student  implements Comparable
{
     ......
      public  int compareTo(Object obj)
     {
           if(!(obj  instanceof(Student))
                throw  new RuntimeException("不是学生对象");
          Student s = (Student)obj;
           if( this.age>s.age)
                return 1;
           else  if( this.age == s.obj)
          {
                return  this.name.compareTo(s.name); // String类实现了Comparable接口
          }
           else
                return -1;
     }
}
底层数据结构是二叉树 
8 实现Comparator方式排序

当元素自身不具备比较性,或者具备的比较性不是所需要的。
这时需要让容器自身具备比较性。
定义了比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。

当两种排序都存在时,以 比较器为主。
定义一个 ,实现 Comparator接口,覆盖 compare方法。

class MyCompare  implements Comparator
{
      public  int compare(Object o1, Object o2)
     {
          Student s1 = (Student)o1;
          Student s2 = (Student)o2;
          
           int num = s1.getName().compareTo(s2.getName);
           if(num == 0)
          {
               new Integer(s1.getAge()).compareTo( new Integer(s2.getAge());
          }
           return num;
     }
}

TreeSet ts =  new TreeSet( new MyCompare());
TreeSet练习
     按照字符串长度排序
       字符串本身具备比较性。但是它的比较方式不是所需要的。
       这时就只能使用比较器
9 泛型
泛型:JDK1.5版本以后出现新特性。用于解决安全问题,是一个类型安全机制。

好处
1.将运行时期出现问题ClassCastException,转移到了编译时期。,
     方便于程序员解决问题。让运行时问题减少,安全。,
2,避免了强制转换麻烦
泛型的特点
提高了程序的安全性
将运行期遇到的问题转移到了编译期
省去了类型强转的麻烦
泛型类的出现优化了程序设计
泛型格式:通过<>来定义要操作的引用数据类型。

在使用java提供的对象时, 什么时候写泛型呢?

通常在集合框架中很常见,
只要见到<>就要定义泛型。

其实<> 就是用来接收类型的。
当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。
例程
1.
ArrayList<String> al =  new ArrayList<String>();
Iterator<String> it = al.iterator();

TreeSet<String> ts =  new TreeSet<String>( new LenComparator());

Iterator<String> it = ts.iterator();

class LenComparator  implements Comparator<String>
{

}
2.
TreeSet<String> ts =  new TreeSet<String>( new LenComparator());
Iterator<String> it = ts.iterator();

class LenComparator  implements Comparator<String>
{
      public  int compare(String o1,String o2)
     {
          ......
     }
}
3 泛型类。
什么时候定义泛型类?
当类中要操作的引用数据类型不确定的时候,
早期定义Object来完成扩展。
现在定义泛型来完成扩展。
class Utils<QQ>
{
      private QQ q;
      public  void setObject(QQ q)
     {
           this.q = q;
     }
      public QQ getObject()
     {
           return q;
     }
}
class  GenericDemo3
{
      public  static  void main(String[] args)
     {
          Utils<Worker> u =  new Utils<Worker>();

          u.setObject( new Student()); // 编译失败
          Worker w = u.getObject();    
     }
}
4 泛型方法
泛型类定义的泛型,在整个类中有效。如果被方法使用,
那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。
为了让不同方法可以操作不同类型,而且类型还不确定。
那么可以将泛型定义在方法上。
特殊之处:
静态方法不可以访问 类上定义的泛型
如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上。
class Demo<T>
{
      public   void show(T t)
     {
          System.out.println("show:"+t);
     }
      public <Q>  void print(Q q)
     {
          System.out.println("print:"+q);
     }
      public   static <W>  void method(W t)
     {
          System.out.println("method:"+t);
     }
}
5 泛型定义在接口上
interface Inter<T>
{
      void show(T t);
}

/*
class InterImpl implements Inter<String>
{
     public void show(String t)
     {
          System.out.println("show :"+t);
     }
}

*/
// 实现的时候也不知道类型
class InterImpl<T>  implements Inter<T>
{
      public  void show(T t)
     {
          System.out.println("show :"+t);
     }
}
class GenericDemo5
{
      public  static  void main(String[] args)
     {

          InterImpl<Integer> i =  new InterImpl<Integer>();
          i.show(4);
           // InterImpl i = new InterImpl();
          
// i.show("haha");
     }
}
6.泛型限定
? 通配符。也可以理解为占位符。
泛型的限定;
? extends E: 可以接收E类型或者E的子类型。上限。
? super E: 可以接收E类型或者E的父类型。下限
7.应用
Comparator<E>   
public TreeSet(Comparator<? super E> comparator)//可以接收E和E的父类