java集合框架->Collection

来源:互联网 发布:驱魔少年网络大电影 编辑:程序博客网 时间:2024/04/19 23:49

一、集合框架总体结构图

    集合也成为容器,容器的区分,因每一个容器的数据结构不同。数据结构是数据存储的一种方式。

二、数组与集合类的区别

         集合:长度可变,数组长度不可变

         数组:存储同一数据尅行元素,,可存储基本数据类型值。

         集合:存储的都是对象,而且类型可以不一致。

三、Collection集合

         1、Collection是集合框架的常用接口,旗下有两个子接口:List(列表),Set(集)。

                Collection

                     ↓→List元素有序(有序指的是存储的与取出的顺序),因为该集合体系有索引,↓此接口可得用户可以对列表中的每个元素的插入位置进行精确地控制,元可                                 ↓  重复。

                     ↓→Set元素无序,不包含重复的元素集合,而且方法与Collection一致。

         2、方法

          ▲add(Object obj);添加元素

          ▲remove(Object obj);删除元素

          ▲removeAll(Collection<?>  c)调用者只保留另一集合中没有的元素

          ▲clear();清空集合

          ▲Contains(Object obj);判断是否存在obj这个元素

          ▲isEmpty();判断是否为空

          ▲size();获取个数,集合长度

          ▲retainAll(Collection  c)调用者只能保留亮子河的共性元素

         ★集合中存储的都是对象的引用(地址)

         ●集合的取出方式:每个容器存储的结构不同,所以取出的细节也不一样,但都有共性的内容,判断和取出,便可以把共性内容抽取出来,只需要符合同一个规则,该规则就是迭代器(Iterator),通过对外提供方法:iterator()获取集合的元素。

四、迭代器

         迭代的常见操作

                   ▲hashNext();判断是否有下一个元素

                   ▲next();取出下一个元素

                   ▲remove();移除迭代器返回的最后一个元素      

         例如:

                   第一种遍历方式:

 Collection  c  =new ArrayList();//创建一个集合                   Iterator  it =  c.iterator();//获取一个迭代器,用于取出集合中的元素                        While(it.hasNext()){                            It.next();                 }        

第二种遍历方式:

  for(Iteratoriter = a.iterator();iter.hasNext();  ){                    System.out.println(iter.next());           }

★迭代器在Collcection接口中是通用的,它替代了Vector类中的Enumeration(枚举)。迭代器的next方法是自动向下取元素,要避免出现NoSuchElementException。迭代器的next方法返回值类型是Object,所以要记得类型转换。

五、List集合

List:元素是有序的,元素可以重复。因该集合底层是数组,有索引,该集合特有的方法都↓是围绕索引而定义的,List获取元素的方式有两种,一种是迭代,另一种是遍 
↓          历+get()方法                                                                                                                                                      

↓→ArrayList:底层数据结构使用的是数组结构。特点:查询速度快,但是增删稍慢,线程不同步                                                                   

↓→Vector:底层是数组结构,线程同步,低效率(被ArrayList替代)                    

↓→LinkedArrayList:底层使用的是链表数据结构,特点增删速度快,查询慢。

方法:

       ▲add(index,E element)在指定位置添加元素                                       

       ▲addAll(index,Collection <? Extends E>c)    添加指定 collection 中的所有元素到此列表的结尾                                                                       

       ▲E set(index, E element)          修改指定位置的元素                            

       ▲E get(index)   返回列表中指定位置的元素                                   

       ▲List<E> subList(int fromIndex,int toIndex)  返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图                                                                      

       ★ListIterator<E>   ListIterator(intindex);List特有迭代器                                            

       ★intindexOf(Object obj);获取元素第一次出现的位置,如果没有则返回-1             Object[]  toArray()返回按适当顺序包含列表中的所有元素的数组(从第一个元素到最后一个

           元素)。                                                                   

      ★<T>  T[]  toArray(T[]  a)  返回按适当顺序(从第一个元素到最后一个元素)包含列表中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。        

      ★如果用迭代器Iterator获取集合中的元素同时对集合中元素进行增删操作,会出现什么情况呢?      

  Iterator it  =  c.iterator();//获取一个迭代器,用于取出集合中的元素                                        While(it.hasNext()){                                            If(“itcast”.equals(obj)){                                                   List.add(“java”);   //添加元素                                           }                                }     

此种方式将会出现并发修改异常:ConcurrentModificationException当方法检测到对象的并发修改,但不允许这样修改时,抛出磁异常。

解决方案:可用迭代器的方法操作,可迭代器的方法只有判断获取,删除,但Iterator有子接口可以解决

         接口:ListIterator→该列表迭代器只有List集合有

ListIterator<E>   ListIterator(intindex)

★ListIterator迭代器

List集合特有迭代器ListIterator,是Iterator的子接口。

特有方法:

▲add(Object obj)添加元素

▲set(E e)   用指定元素替换 nextprevious 返回的最后一个元素

▲hasNext()    以正向遍历列表时,如果列表迭代器有多个元素

▲hasPrevious()如果以逆向遍历列表,列表迭代器有多个元素,则返回true

▲E  precvious()    返回列表中的前一个元素。

List集合可存储重复元素,如果需求中要求容器中的元素保证唯一性,如何解决?

         思路:①先创建一个临时容器,用于存储唯一元素。

                     ②遍历原容器,将遍历到的元素存储到临时容器中去判断是否存在。

                     ③如果存在,不存储到临时容器,如果不存在,存储到临时容器中。

                     ④遍历结束后,临时容器存储的就是唯一性的元素,如果将唯一性元素保留到原容器中,只要将临时容器中的元素添加到原容器中。

对于list集合,底层判断元素是否相同,其实用的是元素自身的equals方法完成的。所以建议元素都要复写equals方法,建立元素对象自己的比较相同的条件依据

ArrayList

         ArrayList<E>:List接口的大小可变数组的实现,是数组结构,长度可变,查询快(相对而言)增删慢,不同步。

         特有方法:

         ★Object[ ]  toArray()   按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组

         ★<.T>T[ ] toArray(T[ ] a)  按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。

LinkedList<E>

         LinkList<E>:List接口的链接列表实现,允许所有元素(包括null),链表结构,增删快,查询慢,可用于实现堆栈,队列。

        ★addFirst(E  e)   将指定元素插入此列表的开头

        ★addFirst(E  e)        将指定元素添加到此列表的结尾

 

获取元素与删除元素,如果集合中没有元素,会出现NoSuchElementException异常

       ★E  getFirst()返回此列表的第一个元素

       ★E  getLast()返回此列表的最后一个元素。

      ★E  removeFirst()  移除并返回此列表的第一个元素

      ★E  removeLast()  移除并返回此列表的最后一个元素。

但在JDK1.6后出现了替代方法

     ★E   peekFirst()  获取但不移除此列表的第一个元素;如果此列表为空,则返回null

     ★E  peekLast()   获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null

     ★E  offerFirst()   在此列表的开头插入指定的元素。

     ★E  offerLast()   在此列表末尾插入指定的元素。

     ★E  pollFirst()   获取并移除此列表的第一个元素;如果此列表为空,则返回 null

     ★E  pollLast()   获取并移除此列表的最后一个元素;如果此列表为空,则返回 null

例如:用LinkedList模拟一个堆栈或者队列数据结构,创建一个堆栈或者队列数据结构对象,该对象中使用LinkedList完成。

publicclassLinkListDemo {       publicstaticvoid main(String[] args) {              //创建一个队列对象。              Queue que = new Queue();              //往队列中添加元素。              que.addDemo("first");              que.addDemo("second");              que.addDemo("third");              while(!que.isEmptyDemo()){                     System.out.println(que.myGet());              }       }    }class Queue{       //封装了一个链表数据结构。       privateLinkedListlinklist;       /*        * 队列初始化时,对链表对象初始化。        */       public Queue() {              linklist =newLinkedList();       }       /**        * 队列的添加元素功能。        */       publicvoidaddDemo(Object obj){              linklist.addFirst(obj);       }       /**        * 队列的获取方法。        */       public Object myGet(){              returnlinklist.removeLast();       }       /**        * 判断队列中元素是否空,没有元素就为true。        */       publicboolean isEmptyDemo(){              returnlinklist.isEmpty();       }}

Vector<E>

底层数据结构为数组,线程安全,效率低。

Vector有三种取出方式,枚举是Vector特有的取出元素方式,因为枚举的名称以及方法的名称过长,所以被迭代器取代了,

特有方法:

      addElement(Ee)   添加元素

      Enumeration<E>   elements()  Vector特有取出方法枚举

枚举Enumeration中的方法:

      hasMoreElements()   相当于Iterator迭代器中的hasNext()

      nextElements()   相当于Iterator中的next()方法

六、Set集合

一、概述:

Set:元素无序(存入和取出顺序宝宝正一定),元素不可以重复,而且方法和Collection一↓  致,Set集合取出元素的方法(迭代器)

↓→HashSet:底层数据结构是哈希表,线程不同步,保证元素唯一性的原理:判断元素的↓ hashCode值是否相同,如果相同,还会继续判断元素的equals方法是否为true

↓→TreeSet:可以对Set集合中的元素进行排序,默认按照的自然排序,或者根据创建Set时提供的Comparator进行排序,具体取决于使用的构造方法。底层数据是二叉树,保

     证元素唯一性的依据:comepareTo方法return 0

二、HashSet

       线程不安全,(与数组相比查找速度更快,不需要遍历),保证元素唯一性依赖于hashCode()equals()方法。

哈希表示意图:


问题:存储引用类型时没有保证唯一性原因分析。

       分析:存储元素时先调用了元素对象的hashCode()方法,而每个引用对象都是新建立的对象,所以hashCode值不同,也就不需要判断equals()方法。

       解决方案:想要保证对象的唯一性,不能使用Object中的hashCode方法,需要重写hashCode算法内容,并同时重写equals方法。

       例如:

publicclassHashSetDemo {       publicstaticvoid main(String[] args) {              HashSet<Student> hashset =new HashSet<Student>();              hashset.add(new Student("lisi", 19));              hashset.add(new Student("lisi2", 19));              hashset.add(new Student("lisi3", 40));              hashset.add(new Student("lisi", 21));              hashset.add(new Student("lisi", 19));              for (Iterator<Student> iterator =hashset.iterator(); iterator.hasNext();) {                     Student student =iterator.next();                     System.out.println(student.getName()+"---"+student.getAge());              }       }}class Student {       //定义学生姓名       private Stringname;       //定义学生年龄       privateintage;       public Student(String name,int age) {              this.name = name;              this.age = age;       }       public String getName() {              returnname;       }       publicintgetAge() {              returnage;       }       @Override       /*        *重写hashCode方法,建立Student对象的hash算法内容,通过学生对象持有数据姓名和年龄值来算出hash值。        */       publicinthashCode(){              finalint prime= 24;              returnname.hashCode() +age * prime;//乘以24尽量保证hash值不同       }       publicbooleanequals(Object obj){              System.out.println("equals");              if(this ==obj)                     returntrue;              if(!(objinstanceof Student)){                     thrownewClassCastException();              }              Student stu = (Student)obj;              returnthis.name.equals(stu.name) && this.age == stu.age;       }}

三、LinkedHashSet

具有可预知迭代顺序的Set接口的哈希表和链接列表实现(提高元素查询效率的前提下还想有序情况下可使用)

四、TreeSet

概述:底层的数据结构为二叉树结构(红黑树),使用元素的自然排序进行排序,是因为TreeSet类实现了Comparable接口,该接口强制让增加到集合的对象进行比较,需要重写compareTo方法,才能让对象按指定需求(入人的年龄大小比较等)排序,或者根据创建Set时提供的Comparator进行排序,具体取决于使用的构造方法,可以对Set集合中的元素进行排序。

在集合中存储对象时,通常该对象都需要覆盖hashCodeequals方法,同时实现

Comparable接口,建立对象的自然排序,通常还有一个方法也会重写toString()。

二叉树示意图:


保证数据唯一性的依据:通过compareTo方法的返回值,是正整数、负数或零。

TreeSet排序的两种方式:

1、  自然排序

让元素具备比较性,需实现Comparable接口,覆盖compareTo方法,

publicclassTreeSetDemo {       publicstaticvoid main(String[] args) {                           TreeSet<Object> tree =new TreeSet<Object>();              tree.add(new Student("lisi", 36));              tree.add(new Student("lisi2", 19));              tree.add(new Student("lis3", 125));              tree.add(new Student("lisi", 22));                           for (Iterator<Object> iterator = tree.iterator();iterator.hasNext();) {                     Object object = iterator.next();                     System.out.println(object);              }                    }} class Studentimplements Comparable<Object>{       //定义学生姓名       private Stringname;       //定义学生年龄       privateintage;       public Student(String name,int age) {              this.name = name;              this.age = age;       }       public String getName() {              returnname;       }       publicintgetAge() {              returnage;       }       /**        * 重写compareTo方法,建立学生的自然排序(对象的默认排序方式)。        * 按照学生年龄排序。        */       @Override       publicintcompareTo(Object o){              if(!(oinstanceof Student)){                     thrownewClassCastException();              }              Student stus = (Student)o;                           int temp =this.age - stus.age;              return temp == 0 ? temp =this.name.compareTo(stus.name):temp;       }       @Override       public String toString() {              return name+"---"+age;       }}

第二种方式:比较器排序

       如果元素自身具备自然排序,实现了Comparable接口重写了compareTo方法,如果元素不具备自然排序,或者具备自然怕许布施说需要的,这是只能用第二种方式。比较器排序,其实就是在创建TreeSet集合时,在构造函数中指定具体的比较方式,那就是需要定义一个类实现Comparator接口,重写compare方法。

  publicclassTreeSetComparator {       publicstaticvoid main(String[] args) {              TreeSet set =new TreeSet(new ComparatorByName());              set.add(new Student("lisi6",21));              set.add(new Student("lisi8",22));              set.add(new Student("lisi5",25));              set.add(new Student("lisi3",23));              set.add(new Student("lisi7",20));              for (Iterator it = set.iterator();it.hasNext();) {                     Student ss = (Student)it.next();                     System.out.println(ss.getName()+"---"+ss.getAge());              }       }}class ComparatorByNameimplementsComparator{        @Override       publicintcompare(Object o1, Object o2) {              Student stu1 = (Student)o1;              Student stu2 = (Student)o2;              int temp =stu1.getName().compareTo(stu2.getName());                           return temp == 0 ? stu1.getAge()-stu2.getAge() :temp;       }}class Student {       private Stringname;       privateintage;       public Student() {              super();       }       public Student(String name,int age) {              super();              this.name = name;              this.age = age;       }       public String getName() {              returnname;       }       publicintgetAge() {              returnage;       }}

★简化集合遍历的语法

Foreach循环使用,其实就是增强fou循环。

格式:for(元素数据类型变量 Collection或者数组){

       用于遍历Collection集合或数组,通常只能遍历元素不要在遍历元素过程中做对集合元素操作。

}

与老式for循环区别:

       Foreach循环必须有被遍历的目标,目标只能是Collection及其子类或者是数组,遍历数组时候,如果仅为遍历,可以使用增强for,如果要对书序的元素进行操作要使用老式for循环,因可以通过角标操作。

 

 

 

 

 

 



0 0
原创粉丝点击