关闭

Java基础日记———Collection

标签: 集合程序员语言java
152人阅读 评论(0) 收藏 举报
分类:
一.概述
·为什么出现集合类?
    面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式。
    数组和集合类同是容器,有何不同?
    数组虽然也可以存储对象,但长度是固定的;集合长度是可变的。数组中可以存储基本数据类型,集合只能存储对象。
·集合类的特点
    集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。

集合存储数据的方式不同,即数据结构不同。

(1)add方法的参数类型是Object。以便于接收任意类型对象。

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

二.Collection体系结构
Collection常用的两个子接口:
    List:元素有序,可重复,因为其中元素有索引。 
    set: 无序(无下标),不可重复。
    (栈内存主要存储基本类型和引用,而堆存储new出来的东西和数组.)
(1)共性方法
    添加
         add(e):确保此 collection 包含指定的元素(可选操作)。
         addAll(collection);将指定 collection 中的所有元素都添加到此 collection 中(可选操作)。
(2)删除
         remove(e):移除集合里的某元素
         removeAll(collection);移除指定的集合
         clear():清空集合
(3)判断。
         contains(e):是否包含该元素
         isEmpty();
(4)获取
         iterator():返回在此 collection 的元素上进行迭代的迭代器。
         size():集合长度。
(5)获取交集。
         retainAll(Collection<?> c);仅保留此 collection 中那些也包含在指定 collection 的元素。
(6)集合变数组。
         toArray():返回包含此 collection 中所有元素的数组。
共性方法较简单,只举一个例子做演示。

代码示例:

    ArrayList a1 = new ArrayList<>();
    ArrayList a2 = new ArrayList<>();
    a1.add("java01"); a1.add("java01"); //添加元素
    a1.add("java02");  a1.add("java03");
    a2.add("java04");  a2.add("java02"); 
    a2.add("java01");
    a1.remove(2);
    a1.remove("java01");
    a1.clear();
    a1.retainAll(a2);//去除相同部分
其中Iterate()做重点介绍。
1.Iterator迭代器
    什么是迭代器呢?
    其实就是集合的取出元素的方式。
    如同抓娃娃游戏机中的夹子。
    迭代器是取出方式,会直接访问集合中的元素。
    所以将迭代器通过内部类的形式来进行描述。
    通过容器的iterator()方法获取该内部类的对象。

原理图:这里写图片描述

2.List
    List:元素是有序的,元素可以重复。因为该集合体系有索引。
        |--ArrayList:底层的数据结构使用的是数组结构。特点:查询速度很快。但是增删稍慢。线程不同步(线程无联系),不安全。
        |--LinkedList:底层使用的链表数据结构。特点:增删速度很快,查询稍慢。线程不同步。
        |--Vector:底层是数组数据结构。线程同步。被ArrayList替代了。因为效率低,但是安全
    (List集合判断元素是否相同,依据是元素的equals方法。当存储时会自动调用。)
 List的特有方法:凡是可以操作角标的方法都是该体系特有的方法。
    增
        add(index,element);
        addAll(index,Collection);
    删
        remove(index);
    改
        set(index,element);
    查
        get(index):获取角标
        subList(from,to):截取
        ListIterator():获取迭代器
        int indexOf(obj):获取指定元素的位置。
List集合特有的迭代器。ListIterator是Iterator的子接口。
    在迭代时,不可以通过集合对象的方法操作集合中的元素。
    因为会发生ConcurrentModificationException异常。
    所以,在迭代器时,只能用迭代器的放过操作元素,可是Iterator方法是有限的,
    只能对元素进行判断,取出,删除的操作,
    如果想要其他的操作如添加,修改等,就需要使用其子接口,ListIterator。
    该接口只能通过List集合的listIterator方法获取。

代码示例:

    ArrayList a1 = new ArrayList<>();
    ListIterator li = a1.listIterator();        
    while(li.hasNext()){
            Object obj = li.next();
        if(obj.equals("Java01"))
        li.set("Java06");
    }
    while(li.hasPrevious()){
        System.out.println("pre"+li.previous());//返回列表中的前一个元素。
    }

(1)LinkeList  
    底层使用的链表数据结构。特点:增删速度很快,查询稍慢。线程不同步。
    特有方法:
    addFirst();  addLast();
    getFirst(); getLast();
    获取元素,但不删除元素。如果集合中没有元素,会出现NoSuchElementException
    removeFirst(); removeLast();

    LinkedList的部分方法已经升级。JDK1.6以后。
    removeLast();
    获取元素,但是元素被删除。如果集合中没有元素,会出现NoSuchElementException
    pollLast();
    获取元素,但是元素被删除。如果集合中没有元素,会返回null。

代码示例:

      LinkedList link = new LinkedList();
        link.addLast("java01");
        link.addLast("java02");
        link.addLast("java03");
        System.out.println(link.getFirst());//输出第一个元素
        System.out.println(link.removeLast());//移除最后一个  
(2)ArrayList   
    底层的数据结构使用的是数组结构。特点:查询速度很快。但是增删稍慢。线程不同步(线程无联系),不安全。

代码示例:去掉重复数据

        ArrayList<String> al = new ArrayList<>();
al.add("PHP1");
al.add("PHP2");
al.add("PHP3");
al.add("PHP4");
al.add("PHP1");
al.add("PHP2");
al.add("PHP3");
        ArrayList<String> al = new ArrayList<>();
        Iterator<String> it =al.iterator();
while(it.hasNext()){
String obj = it.next();
if(!nal.contains(obj))
                nal.add(obj);
            }
(3)Vector 
    底层是数组数据结构。线程同步。被ArrayList替代了。因为效率低,但是安全。
    枚举就是Vector特有的取出方式。
    发现枚举和迭代器很像,其实枚举和迭代是一样的。
    但是因为枚举的名称以及方法的名称都过长,所以被迭代器取。

代码示例:

         Vector v = new Vector();
v.add("Ruby01");
v.add("Ruby02");
v.add("Ruby03");
Enumeration en = v.elements();
while(en.hasMoreElements()){
System.out.println(en.nextElement());
}
3.Set
Set:元素是无序,元素不可以重复。
     共性方法不做缀叙。
    (1)HashSet:底层数据结构是哈希表。是线程不同步的。不安全。其中的元素是唯一的。
        HashSet是如何保证元素唯一性的呢?
        当哈希表中出现两个相同的对象时:要先判断hashCode值是否相同,若相同,再使用equals判断对象是否相同。
        若不是一个对象,那么这个会对象存储顺延,还在原位置。
        其中涉及两个方法:hashCode和equals来完成。
        如果元素的HashCode值相同,才会判断equals是否为true。
        如果元素的hashcode值不同,不会调用equals。
        注意,对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashcode和equals方法。 
        那么当hashSet中不允许存入相同的对象时必须复写hashcode方法和equals方法。
        判断删除:先判断hash值,然后判断内容。
    (2)TreeSet:可以对set集合中的元素进行自动排序。自动调用的CompareTo方法。当所传的元素不具备可比性时,需要复写CompareTo方法。
    底层数据结构是二叉树,保证元素的唯一性的依据:compareTo方法return 0。
    TreeSet会进行自动排序,方式有两种。
        第一种方式:让元素自身具备比较性。元素需要覆盖CompareTo方法。

代码示例:

            class Student implements Comparable{//实现Comparable接口。
private String name;
private int age;
public Student(String name,int age) {
this.name = name;
this.age = age;
}
            public String getName() {
return name;
}
             public int getAge() {
return age;
        }
public int compareTo(Object o) {//覆盖compareTo方法。
if(! (o instanceof Student))
throw new RuntimeException();
Student s = (Student) o;
System.out.println(this.name+" compare to "+s.name);
if(this.age>s.age)
return 1;
if(this.age==s.age){
return this.name.compareTo(s.name);
}
return -1;
}
           }
       解析:当Student类需要存入TreeMap中而且排序方式是name与age都比较时,就需要在定义Student 类时覆盖compareTo方法。

   第二种排序方式:当元素不具备比较性时,或者具备的比较性不是所需要的,这时就需要让集合自身具备比较性。
   集合一初始化时,就有了比较方式:即修改其构造函数。
  定义比较器,将比较器对象作为参数传递给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)
                return new Integer(s1.getAge()).compareTo(s2.getAge());
            return num;
                }
                }
    解析:第二种方法实现Comparator接口,复写compare方法。当需要这个比较器是就可以将其传递给需要的集合。
           即为:TreeSe<Student>t ts = new TreeSet<Student>(new MyCompare());
4.泛型概念
JDK1.5 以后出现的新特征,用于解决安全问题。
 好处:
    将运行时期的问题提到编译时期。
    避免了强制转换。
 格式:通过<>来定义操作的引用数据类型。
 (1)泛型定义在类上,定义在方法上,也可以定义在接口上。

代码示例:

       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);
        }
       }

   class Demo 
        {
            public static void main(String[] args) 
            {
                Demo <String> d = new Demo<String>();
                d.show("haha");
                d.print(5);
                d.print("hehe");
                Demo.method("hahahahha");
            }
        }
当类中要操作的引用数据类型不确定时,可以使用泛型类。
     泛型类定义的泛型,在整个类中有效,如果方法被使用,那么泛型类的对象明确要操作的具体类型后,所有操的类型就固定了。即类定里面的方法操作的数据类型就确定了。    
     为了让不同的方法可以操作不同的类型,而且类型还不确定。那么可以将泛型定义在方法上。
 特殊之处:    
      静态方法不可以访问类上确定的泛型。
      如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上。
(2)泛型限定:
    ? 通配符,或者为占位符。
    泛型的限定:? extends E:可以接受E类型或者E的子类型。(上限)
                ? super E 可以接受E类型或者E的父类型。(下限)
    ArrayList<? extends Person>
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:2188次
    • 积分:149
    • 等级:
    • 排名:千里之外
    • 原创:13篇
    • 转载:0篇
    • 译文:0篇
    • 评论:0条
    文章分类
    文章存档