zoukankan      html  css  js  c++  java
  • 11、集合

    集 合

    1、概述

        集合类,API的常用对象,其实是一个容器。

        集合用于存对象,是能存储对象的对象。

        数组也能存,但数组固定长度,而集合不固定长度。

     

    2、集合的由来:

        对象用于封装特有数据,对象多了需要存储,如果对象的个数不确定,就使用集合容器来进行存储。

     

    3、集合的特点:

        *用于存储对象的容器

        *集合的长度是可变的

        *集合中不可以存储基本数据类型值。

          共享数据都存储在方法区里的

          集合容器因为内部的数据结构不同,有多种具体容器。不断的向上抽取,就形成了集合框架。

     

    4、Java中集合类的关系图

     

    5、集合框架的顶层Collection接口:

      (1)Collection接口常用方法:

         添加

          boolean add(Object obj)

          boolean addAll(Collection coll);

     

        删除

          boolean remove(Object obj)

          boolean removeAll(Collection coll);

          void clear();

     

        判断

          boolean contains(Object obj)

          boolean containsAll(Collection coll);

          boolean isEmpty()判断集合中是否有元素

     

        获取:

          int size():

          Iterator接口:对Collection进行迭代的迭代器,取出元素的方式:迭代器

     

        其他

          boolean retainAll(Collection coll);取交集

          Object[] toArray():将集合转成数组;

     

        集合的remove方法会改变集合的长度。

     

    5、迭代器

      迭代器其实就是集合的取出方式。把取出元素的方式定义在集合内部,这样取出方式就可以直接访问集合内部的元素。

      该对象必须依赖于具体容器,因为每一个容器的数据结构都不同。所以该迭代器对象是在容器中进行内部实现的,对于使用容器者而言,具体的实现不重要,只要通过容器获取到该实现的迭代器的对象即可,也就是iterator方法。

      Iterator接口就是对所有的Collection容器进行元素取出的接口

      使用了Collection中的iterator()方法获取集合中的迭代器对象。

      Iterator it = coll.iterator();   

     

    6、Collection下体系:

      *List:有序(存入和取出的属性一致),元素都有索引,元素可以重复

      *Set:元素不能重复,无序。

     

      (1)List集合

        a、List:特有常见方法:有一个共享特点就是都可以操作脚标

          *添加

            void add(index,element)

            void add(index,collection)

     

          *删除

            Object remove(index);

     

          *修改

            Object set(index,element)

     

          *获取:

            Object get(index);

            int indexOf(Object);

            int lastIndexOf(Object)

            List subList(from,to)

     

        b、List下属子类:

          *Vector:内部是数组数据结构,同步的。增删,查询都很慢。

            **Vector如果存储元素超过默认长度时,会以100%长度扩展。

            **Vector特有的方法:枚举

              ***通过elements()方法获取组件的枚举。

              ***枚举是Vector特有的取出方式。枚举和迭代器实质是一样的,但是其名称及方法名过长,被迭代器取代了。  

          *ArrayList:内部是数组数据结构,是不同步的,替代了Vector。查询的速度快。

            **集合长度不是固定的,ArrayList()创建时数字默认长度是10,当存储元素超过10时,会以50%的长度进行扩展。将原来元素复制到新数组中,将新元素往后添加。                          

          *LinkedList:内部是链表数据结构,是不同步的。增删元素的速度很快。

             **LinkedList是一个双向队列集合,特有方法就是可以直接对集合首尾进行添加、获取和删除操作。

             **LinkedList例子:

       

    import java.util.LinkedList;
    
    
    /**
    
     * 使用LinkedList模拟一个堆栈或者队列数据结构
    
     * 
    
     * 堆栈:先进后出
    
     * 队列:先进先出
    
     * @author xiuyuan
    
     *
    
     */
    
    class DuiLie{
    
      private LinkedList link;
    
     
    
        DuiLie(){
    
            link = new LinkedList();
    
        }
    
     
    
        public void myAdd(Object obj){
    
            link.addFirst(obj);
    
        }
    
     
    
        public Object myGet(){
    
            return link.removeLast();
    
        }
    
     
    
        public boolean isNull(){
    
            return link.isEmpty();
    
        }
    
    }
    
     
    
    public class LinkedListTest {
    
    
      public static void main(String[] args) {
    
        DuiLie dl =  new DuiLie();
     
    
        dl.myAdd("java01");
    
        dl.myAdd("java02");
    
        dl.myAdd("java03");
    
        dl.myAdd("java04");
    
     
    
        while(!dl.isNull()){
      
            System.out.println(dl.myGet());
    
        }
    
      }
    
    }

         

     

        c、迭代器:

          List集合有自身的迭代器,通过listIterator()方法获取该迭代器对象。

          ListIterator是Iterator的子接口,通过该接口,可以对list集合迭代时进行增删改查。

     

      (2)Set集合

        Set:元素不可以重复,是无序的。

        Set接口中的方法和Collection一致

     

        a、HashSet:内部数据结构是哈希表,是不同步的。

          如何保证该集合的元素唯一性呢?

            保证元素唯一性的原理:判断元素的hashCode值是否相同,如果相同,还会继续判断元素的equals方法是否为true。需要注意的是,hashCode和equals不是我们调用的,是底层自己调用的。对于判断元素是否存在以及删除等操作,依赖的都是元素的hashCode和equals。

            记住:如果元素要存储到HashSet集合中,必须覆盖hashCode方法和equals方法。

            一般情况下,如果定义的类会产生很多对象,比如人,学生,书,通常都需要覆盖equals,hashCode方法,建立对象判断是否相同的依据。

          HashSet:因为底层为哈希表结构,存储元素时,会先计算对象的哈希值,按哈希值来确定存储的位置,如果哈希值相同,则会进一步判断对象的内容是否相同,相同则不会存储,如果不同,则会在相同的哈希值位置上顺延下去。 

     

         b、TreeSet:底层是二叉树结构,可以对Set集合中的元素进行排序。是不同步的。

          判断元素唯一性的方式:就是根据比较方法的返回结果是否是0,0表示相同。

          TreeSet对元素进行排序的第一种方式:

            让元素自身具备比较功能,元素需要实现Comparable接口。覆盖compareTo方法,这种方法也称为元素的自然顺序或默认顺序。

    class Student implements Comparable{//该接口强制让学生具备比较性
    
      private String name;
    
      private int age;
    
     
    
      Student(String name,int age){
    
        this.name = name;
    
        this.age = age;
    
      }
    
     
    
      public int compareTo(Object obj){
    
        if(!(obj instanceof Student)){
    
            throw new RuntimeException("不是学生对象");
    
        }
    
     
    
        Student stu = (Student)obj;
    
     
    
        System.out.println(this.name+"...compareto..."+stu.name);
    
     
    
        if(this.age == stu.age){
    
            return this.name.compareTo(stu.name);
    
        }
    
        return this.age - stu.age;
    
      }
      
    public String getName(){     return name;   }   public int getAge(){   return age;   } }

     

          TreeSet对元素进行排序的第二种方式:

            当元素自身不具备比较性时,或具备的比较性不是所需要的,这时就需要让集合自身具备比较功能,定义一个类实现Comparator接口,覆盖compare方法。

            将该类对象作为参数传递给TreeSet集合的构造函数。

            当两种比较方式都存在时,以集合的比较方式为主。

    代码示例:

    public class TreeSetDemo2 {
    
     
    
      public static void main(String[] args){
    
        TreeSet ts = new TreeSet(new MyCompare());
    
     
    
        ts.add(new Student("lisi",22));
    
        ts.add(new Student("lisi007",20));
    
        ts.add(new Student("lisi09",19));
    
        ts.add(new Student("lisi08",19));
    
        ts.add(new Student("lisi01",40));
    
     
    
        for(Iterator it = ts.iterator();it.hasNext();){
    
          Student stu = (Student)it.next();
    
          System.out.println(stu.getName()+"..."+stu.getAge());
      
        }
    
      }
    
    }
    
     
    
    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());
      
     
    
        return num==0?s1.getAge()-s2.getAge():num;
    
      }
    
    }

     泛型

     

      JDK1.5版本以后出现的新特性,用于解决安全问题,是一个类型安全机制。

     

      好处:

     

        1、经运行时期出现的问题ClassCastException,转移到了编译时期。方便程序员解决问题,让运行时期问题减少,安全。

     

        2、避免了强制转换的麻烦。

     

     

     

      泛型格式:通过<>来定义要操作的引用数据类型。

     

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

     

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

     

      其实<>就是用来接收类型的。当使用集合时,将集合中要存储的数据类型作为参数传递到<>即可。

     

     

     

    泛型类:

     

      什么时候定义泛型类?

     

        当类中要操作的引用数据类不确定的时候,

     

        早期定义Object来完成扩展

     

        现在定义泛型来完成扩展。

     

     

     

     

     

    泛型方法:

      泛型类定义的泛型,在整个类中有效,如果被方法使用,那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。

      为了让不同方法可以操作不同类型,而且类型还不确定,那么可以将方向定义在方法上。

     

       上图,show方法类型跟着类上定义的泛型,而print方法则有随自己定义的泛型

     

    静态方法泛型:

      特殊之处:静态方法不可以方法类上定义的泛型。

      如果静态方法操作的引用数据类型不确定,可以将泛型定义在方法上

     

     

    注意:方法泛型位置:必须方法修饰符后面,返回值类型前面!

     

    泛型定义在接口上:

     

     

    泛型限定:

      ? 通配符,也可以理解为占位符

    泛型的限定:

      ? extends E:可以接收E类型或E的子类型。上限

      ? super E:可以接收E类型或者E的父类型。下限

     

      泛型通配符<?>叫做占位符,表示不明确类型

     

    上图结果是什么类型都能够传入printColl方法里

    即定义方向为<?>后,什么类型都可以传入

     

     上图即是泛型限定,限定了传入的类型范围,

    <? extends Person> 表示只可以传入Person类或其子类。

     

     

    Map集合

      该集合存储键值对,一对一对往里存,而且要保证键的唯一性

      方法:

        1、添加

          put(K key,V value);添加元素,存入相同键的值时,新值会替代旧值,并且会返回旧值。

          putAll(Map<? extends K,? extends V> m);添加传入的所有元素

     

        2、删除

          clear();从此映射中移除所有映射关系

          remove(Object key);根据键删除元素

     

        3、判断

          containsValue(Object value);判断值

          containsKey(Object key);判断键

          isEmpty();

     

        4、获取

          get(Object key);获取该键对应的值

          size()

          values();获取所有值。返回此映射中包含的值的Collection视图。

          entrySet()

          keySet()

     

    Map:

      |--Hashtable:底层是哈希表数据结构,不可以存入null键null值。该集合是线程同步的。效率低

      |--HashMap:底层是哈希表数据结构,允许使用null键和null值,该集合是线程非同步的。效率高

      |--TreeMap:底层是二叉树结构,线程不同步,可以用于给Map集合中的键进行排序。

     

      和Set很像,其实Set底层就是使用了Map集合。

      可以通过get方法的返回值来判断一个键是否存在,通过返回null来判断

     

    Map集合的两种取出方式:

      Map集合的取出原理:将map集合转成set集合,再通过迭代器取出。

        1、Set<k> keySet:将map中所以的键存入到Set集合中,因为Set集合具备迭代器,所以可以通过迭代方式取出所有的键,然后通过get方法获取每一个键对应的值

    //先获取map集合中的所有的键的set集合keySet();方法
    
    Set<String> keySet = map.keySet();
    
     
    
    //通过Set集合,就可以获取其迭代器
    
    for(Iterator<String> it = keySet.iterator();it.hasNext();){
    
      String key = it.next();
    
      //有了键就可以通过map集合的get方法获取其对应的值
    
      String value = map.get(key);
    
      System.out.println(key+":"+value);
    
    }

     

        2、Set<Map.Entry<K,V>> entrySet:将map集合中的映射关系存入到了set集合中而这个关系的数据类型就是Map.Entry。

    //将map集合中的映射关系取出
    
    Set<Map.Entry<String, String>> set = map.entrySet();
    
    for(Iterator<Map.Entry<String,String>> it = set.iterator();it.hasNext();){
    
      Map.Entry<String, String> me = it.next();
    
      String key = me.getKey();
    
      String value = me.getValue();
    
      System.out.println(key+":"+value);
    
    }

     

      Map.Entry<K,V>接口:

        Map.Entry 其实Entry也是一个接口,它是Map接口中的一个内部接口。

          该接口是静态修饰的,故一定是内嵌的接口,在成员位置上,才也可以被static修饰符修饰,

     

      什么时候使用Map集合呢?

        当数据之间存在着映射关系时,就要先考虑使用map集合。

     

    Map扩展知识:

      Map集合被使用是因为具备映射关系。

      一对多映射关系

      

    Collections类

      工具类:里面的方法都是静态的,不具备构造函数,不需要被实例化的,因为内部并没有封装特有数据,都是共享方法。

        该类是专门用于对集合进行操作的工具类。

     

      常用方法:

        排序:

          static <T extends Comparable<? super T>> void sort(List<T> list) 根据元素的自然顺序排序,泛型限定要保证元素具备比较性

          static <T> void sort(List<T> list,Comarator<? super T> c)根据指定比较器产生的顺序排序,不需限定元素是否具备比较性。

            public static <Comparable<? super T>> T Max(Collection<? extends T> coll)获取最大元素

            binarySearch(List<? extends Comparable<? Super T>> list,T key) 使用二分查找获取指定对象。

            public static <T> void fill(List<? Super T> list,T obj)是指定元素替换列表中的所有元素。

            replaceAll(List,old,new)替换元素

          reverse(list)将集合元素反转

      

          Comparator<T> reverseOrder();返回一个比较器,强行逆转实现了Comparable接口的对象collection的自然顺序。

           重载的函数。传入比较器,强行将其逆转。

     

      将线程不同步转为同步

    其实底层原理就是加锁,加锁后里面调用的还是原集合中的方法。

        底层源代码:

    static <T> List<T> synchronizedList(List<T> list, Object mutex) {
    
      return (list instanceof RandomAccess ?
    
                    new SynchronizedRandomAccessList<T>(list, mutex) :
    
                    new SynchronizedList<T>(list, mutex));
    
    }
    
     
    static class SynchronizedList<E> extends SynchronizedCollection<E> implements List<E> {
    
      static final long serialVersionUID = -7754090372962971524L;
      
    final List<E> list;
      SynchronizedList(List
    <E> list) {   super(list);    this.list = list;   }   SynchronizedList(List<E> list, Object mutex) { super(list, mutex);   this.list = list; }   public boolean equals(Object o) {        synchronized(mutex) {return list.equals(o);}   }   public int hashCode() {     synchronized(mutex) {return list.hashCode();}   }   public E get(int index) {     synchronized(mutex) {return list.get(index);}   }   public E set(int index, E element) {     synchronized(mutex) {return list.set(index, element);}   }   public void add(int index, E element) {     synchronized(mutex) {list.add(index, element);}   }   public E remove(int index) {     synchronized(mutex) {return list.remove(index);}   }   public int indexOf(Object o) {     synchronized(mutex) {return list.indexOf(o);}   }   public int lastIndexOf(Object o) {     synchronized(mutex) {return list.lastIndexOf(o);}   }   public boolean addAll(int index, Collection<? extends E> c) {     synchronized(mutex) {return list.addAll(index, c);}   }   public ListIterator<E> listIterator() {     return list.listIterator(); // Must be manually synched by user   }   public ListIterator<E> listIterator(int index) {     return list.listIterator(index); // Must be manually synched by user   }   public List<E> subList(int fromIndex, int toIndex) {      synchronized(mutex) {       return new SynchronizedList<E>(list.subList(fromIndex, toIndex), mutex);     }   }

     

    Arrays:用于操作数组的工具类,里面都是静态方法。

      asList:将数组变成list集合

        把数组变成list集合有什么好处?

          可以使用集合的思想和方法来操作数组中的元素。

          注意:将数组变成集合,不可以使用集合的增删方法,因为数组的长度是固定的

     

    如果数组中的元素都是对象,那么变成集合时,数组中的元素就直接转成集合中的元素,如果数组中的元素都是基本数据类型,那么会将该数组作为集合中的元素存在

     

      集合变数组:

        Collection接口中的toArray方法

           1、指定类型的数组到底要定义多长呢?

            当指定类型的数组的长度小于了集合的size,那么该方法内部会创建一个新的数组,长度为集合的size;

            当指定类型的数组的长度大于了集合的size,就不会新创建数组,而是使用传递进来的数组

            所以创建一个刚刚好的数组最优。

          2、为什么要将集合变数组?

            为了限定对元素的操作。不需要进行增删了。

     

    高级for循环:

      格式:

        For(数据类型 变量名:被遍历的集合(Collection)或数组){

        }

      对集合进行遍历时,只能获取元素,不能对集合进行操作。

      迭代器除了遍历,还可以进行remove集合中元素的动作。

      如果使用ListIterator,还可以在遍历过程中对集合进行增删改查的动作。

      传统for和高级for有什么区别呢?

        高级for有一个局限性,必须有被遍历的目标,

        建议:在遍历数组时,还是希望使用传统for,因为传统for可以定义角标。

     

    JDK1.5版本出现的新特性:可变参数,其实就是一种数组参数的简写形式,不用每一次都建立数组对象,只要将要操作的元素作为参数传递即可。隐式将这些参数封装成数组。

      格式:类型...形参名

        方法的可变参数

      在使用时注意:可变参数一定要定义在参数列表的最后面。

     

    JDK1.5版本出现的新特性:StaticImport:静态导入。

      当类名重名时,需要指定具体的包名

      当方法重名时,需要指定具体所属的对象或者类。

      

     

    System类:类中的方法和属性都是静态的

      Out:标准输出,默认是控制台

      Int:标准输入,默认是键盘

      System类包含一些有用的类字段和方法。它不能被实例化。

      描述系统的一些信息

      获取系统属性信息:Properties getProperties();

     

    Runtime类:

      该类中并没有提供构造函数,说明不可以new对象,那么会直接想到该类中的方法都是静态的,发现该类中还有非静态方法,说明该类肯定会提供一个方法提供本类对象,而且该方法是静态的,并返回本类类型对象

      该方法是static Runtime getRuntime();

      由这个特点,可以看出该类使用了单例设计模式。

      每个java应用程序都有一个Runtime类实例,是应用程序能够与其运行的环境相连接。可以通过getRuntime方法获取当前运行时。应用程序不能创建自己的Runtime实例

     

      方法:

        public Process exec(String command);该方法在单独的进程中执行指定的字符串命令。

          会返回一个新的Process对象,用于管理子进程。

          Process类是一个抽象类,里面全都是抽象方法,不能够new对象,并且没有子类。

          Process类里方法:destroy()方法可以杀掉子进程。

     

    Date类:日期类

      Date类表示特定的瞬间,精确到毫秒

      该类可以创建对象,但类中许多方法已过时

      Date类的参考类:DateFormat类,

      DateFormat类是日期/时间格式化子类的抽象类,它以与语言无关的方式格式化并解析日期或时间。

     

     

    SimpleDateFormat类是DateFormat的子类

      该类是一个以与语言环境有关的方式来格式化和解析日期的具体类。

      

     

    通过构造函数就可以将自定义的规则传入

     代码体现:

     

     

    Calendar类

      Calendar类是一个抽象类,(注:Calendar表示日历,Date表示日期)

      可先通过Date类中的方法来寻找Calendar中的字段,Date类中的许多方法被Calendar的字段替代了。

      其直接子类:GregorianCalendar:提供了世界上大多数国家/地区使用的标准日历系统。

      Calendar类不能创建对象,但对外提供了方法获取其对象

      static Calendar getInstance():使用默认时区和语言环境获得一个日历。

      Set(int field,int value)方法可以设定时间

      Add(int field,int amount)方法按照日历的规则,为给定的日历字段添加或减去指定的时间量

     

    public static void main(String[] args) {
    
      Calendar c = Calendar.getInstance();
    
      c.set(2011, 2, 1);
    
      c.add(Calendar.DAY_OF_MONTH, -1);
    
      printCalendar(c);
    
    }
    
    
    /**
    
     * 该方法用于打印Calendar
    
     */
    
    public static void printCalendar(Calendar c){
    
        String[] mons = {"一月","二月","三月","四月"
    
                ,"五月","六月","七月","八月"
    
                ,"九月","十月","十一月","十二月"};
    
        String[] weeks = {"","星期日","星期一","星期二","星期三"
    
                  ,"星期四","星期五","星期六"};
    
     
    
      System.out.println(c.get(Calendar.YEAR)+"年");
      
      System.out.println(mons[c.get(Calendar.MONTH)]);
      
      System.out.println(c.get(Calendar.DAY_OF_MONTH));
    
      System.out.println(weeks[c.get(Calendar.DAY_OF_WEEK)]);
    
      System.out.print(c.get(Calendar.HOUR_OF_DAY)+":"+c.get(Calendar.MINUTE)+":"+c.get(Calendar.SECOND));
    
    }

     

     

    Math类:数学类,工具类,都是静态方法

      方法:

        static double abs(E e)返回绝对值

        Double ceil(double e)方法返回大于指定数据的最小整数。

        Double floor(double e)方法,返回小于指定数据的最大整数。

        Long round(double a)返回最接近参数的long;四舍五入

        Double pow(double a,double b)返回a的b次幂。

        Double random()返回带正号的double,该值大于等于0.0且小于1.0,即随机数

     

  • 相关阅读:
    centos7下更新firefox
    Centos7宽带连接
    CAS和AQS
    java中锁的概念
    并发队列
    Callable和Future
    juc下的并发工具类和线程池
    死锁和线程安全的问题
    HashMap源码分析(java1.8)
    List集合框架面试题
  • 原文地址:https://www.cnblogs.com/zyh-blog/p/3256494.html
Copyright © 2011-2022 走看看