黑马程序员_Java基础_集合框架成员Collection

  一,集合类

C语言中我学习过数据结构,但是在java中的数据结构就是集合对数据的存储方式。学习集合后我的感触就是,集合把数据结构的封装的特别好,其实这些是源于在java中一切事物都是对象,我们不需要管一个方法内部是怎样实现的,我们只要会使用,能够明白其原理就可以了。集合就为我们提供了很多操作数据的方法,这也是这么多人爱好学习java的原因吧。

下面用专业术语对集合的概念做一下总结:

1,为什么出现集合呢?

面向对象语言对事物的体现是以对象的形式,所以为了方便对多个对象进行操作,句对对象进行存储,集合就是存储对象的最常用的一种方式。

2,数组和集合类有什么不同呢?

数组虽然也可以存储对象,但是长度是固定不变的,而集合的长度是可变的。数组可以存储基本数据类型,集合只能存储对象。

3,集合类的特点是什么?

集合只用于存储对象,集合的长度是可变的,集合可以存储不同类型的对象。

 

二,集合框架的体系结构中,我们只学习最常用的几个:


Collection:是集合层次结构的根接口。只是集合框架中的一个。

它的子接口ListSet使我们最常用到的。既然集合是一个容器,那么它里面必定会提诸如增删改查的方法。

Collection中的常用方法:

boolean add(Object obj)add方法的参数的类型是Object,一边存储任意对象

boolean addAll(Collection  c)将集合c添加到coleeection

void clean()移除Collection中的所有元素

boolean contains(Object obj)判断Collection中师傅包含obj对象

boolean contains(Collection c)判断Collection中是否包含集合c

boolean equals(Object obj)判断此collection是否与对象obj相等

int hashCode()获取该集合的哈希值

Boolean isEmpty()判断此collection是否为空

Iterator iterator()获取此Collection的迭代器

boolean remove(Object obj)Collection中移除obj

boolean removeAll(Collection c)移除Collection中那些包含在指定c的元素

boolean retainAll(Collection c)将该Collection中与c中相同元素存储到该Collection

int Size()获取该Collection的元素个数

toArray()将该Collection转换为数组

 

 

ArrayList基本使用方法


public class ArrayListDemo
{
    public static void main(String[] args) {
        //创建一个集合容器,用Collection接口的子类ArrayList
        ArrayList a1 = new ArrayList();
        ArrayList a2 = new ArrayList();
        method2(a1,a2);
        
        
    }
    public static void method2(ArrayList a2,ArrayList a3) {
        a2.add("java01");
        a2.add("java02");
        a2.add("java03");
        a2.add("java04");
        a3.add("java03");
        a3.add("java04");
        a3.add("java05");
        a3.add("java06");
        a2.retainAll(a3);//取两个集合的交集,放到此对象(a2)中
        sop(a2);
        sop(a3);
    }
    public static void base_method(ArrayList a) {
        
        a.add("java01");
        a.add("java02");
        a.add("java03");
        a.add("java04");
        //获取集合长度
        sop("a.size:" + a.size());
        //打印集合
        sop(a);
        //删除集合
        a.remove("java01");
        sop(a);
        sop(a.contains("java02"));
        sop(a.isEmpty());
        a.clear();
        sop("移除后的集合:" + a);
    }
    public static void sop(Object obj) {
        System.out.println(obj);
    }
}


三,Collection的迭代器:

所谓的迭代器其实就是取出元素的方式。每个集合都有自己的取出方式,因为取出操作不足以用一个函数来描述,需要多个功能来体现,所以就将多个功能就封装到一个对象里面去,所以每个集合就有自己的取出方式,每个集合有自己的取出方式的原因是每个集合的数据结构不相同,对数据的存储不相同,所以迭代器必须不同。

获取集合迭代器的方式是:

Iterator it = 集合.iterator();

Iterator类中又有三个方法:

hasNext()判断集合是否有下一个元素

it.next()获取集合的下一个元素

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

注意:迭代器器类Iterator其实是一个集合的内部类,可以对集合的元素直接访问。Iterator迭代器的位置其实是位于两个元素之间的,第一次位于第一个元素之前,所以hasNext()方法可以判断集合是否有下一个元素,next()获取下一个元素并返回该元素。

 

迭代器的使用示例:

public class IteratorDemo
{
    public static void main(String[] args) {
        ArrayList a = new ArrayList();
        a.add("java01");
        a.add("java02");
        a.add("java03");
        a.add("java03");
        /*
        Iterator it = a.iterator();
        while (it.hasNext())
        {
            sop(it.next());
        } */  //这里用for循环比while更优化,原因是用while循环必须要在堆内存中建立一个Iterator对象,这个对象
        //用完之后还存在内存中,而用for循环建立的Iterator对象在栈内训中是个临时变量,用完后会自动消失,更节省内存;
        for (Iterator it=a.iterator() ;it.hasNext() ; )
        {
            sop(it.next());
        }
    }
    public static void sop(Object obj) {
        System.out.println(obj);
    }
}

四,Collection的子接口List集合的几种形式与比较:

1List 集合中的元素是有序的,元素可以重复,因为该体系是有索引的

|--ArrayList:底层的数据结构式数组结构。特点是:查询速度快。但是增删稍慢。线程不同步;

|--LinkedList:底层使用的是链表的数据结构。特点是:增删速度很快,但是查询速度稍慢。

|--Vector:底层是数组数据结构。线程同步。被ArrayList替代了。当然使用过程中建议使用ArrayList,在Java1.1之前只有VectorArrayList是从1.2才开始有的。

 

2List:特有方法,凡是可以操作角标的方法都是该体系特有的方法

 

    addindex,element

    addAll(index,Collextion)

    removeindex;

    setindexelement

    get(index);

    subList(form,to)  包含头不包含尾

    listIterator();

 

3,在迭代过程中,不能通过对集合对象的方法操作集合的元素。因为会发生ConcurrentModifcationException异常。所以,在使用迭代器时,只能用迭代器的方法操作元素,可是Iterator方法是有限的;只能对元素判断,取出,删除的操作。如果想使用其他操作,添加,修改等,就需要使用其子接口ListIterator。该接口只能通过List集合的ListIterator方法获取。


public class ListDemo
{
    public static void sop(Object obj) {
        System.out.println(obj);
    }
    
    public static void main(String[] args) {
        ArrayList a = new ArrayList();
        //添加元素
        a.add("java01");
        a.add("java02");
        a.add("java03");
        a.add("java04");
        sop("原集合:" + a);
        //列表迭代器的使用
        ListIterator li = a.listIterator();
        while(li.hasNext()) {
            Object obj2 = li.next();
            if (obj2.equals("java02"))//如果元素是java02,在后面添加java09元素
            {
                //跌代到java002时,在其后面添加java009
                //li.add("java09");
                //删除java002
                //li.remove();
                //当迭代到java002时将该值设置为修改后
                li.set("修改后");
            }
        }
        sop(a);
        sop(li.hasNext());
        sop(li.hasPrevious());
        while(li.hasPrevious()) {
            sop(li.previous());
        }
    }
}

五,Vector底层的数据结构是数组数据结构,它的用法和ArrayList是一样的,List集合是1.2才出现的,1.2之后才有集合框架的。Vector和ArrayList不同在于,ArrayList集合线程不同步,但是Vector线程同步,Vector在增删改查方面都比ArrayList慢,所以ArrayList替代了Vector,虽然Vector线程同步,但是还是不建议使用,即使是多线程,我们也使用ArrayList,然后自己加锁。加锁的时候我们可以使用java提供的加锁方法。Vector和ArrayList还有一个不同,Vector和ArrayList底层数据结构是数组,而集合是可变数组,ArrayList初始长度是10,当满10后,会new一个新ArrayList,长度50%延长,也就是15,然后将原数组copy到新ArrayList中,在后面添加10以后的元素。而Vector是成100%的延长,比较浪费空间。

 

枚举就是Vector的特有取出方式。其实枚举就是和迭代器一样。

 

Vector的使用方法:

Class VectorDemo {
    public static void main(String[] args) {
        Vector v = new Vector();
        v.add(e1);
        v.add(e2);
        v.add(e3);
        v.add(e4);
        Enumeration en = v.elements();
        while(en.hasMoreElements()) {
            System.out.println(en.nextElement());
        }
    }
}

六,LinkedList使用:

LinkedList底层数据结构也是链表,它的特有方法是:

addFirst(e);将指定元素添加到集合头部

addLast(e);将指定元素添加到集合尾部

 

getFirst();获取集合头部的元素,不删除

getLast();获取集合尾部的元素,不删除

 

removeFirst();获取头部元素并移除集合头部那个元素

removeLast();获取尾部元素并移除集合尾部那个元素

 

LinkedList集合的get和remove方法在使用时,当集合中元素为空时会抛出NoSuchElementException,所以在jdk1.6中出现了替代getFirst(),getLast(),removeFirst(),removeLast()方法的方法,他们分别是:

 

offerFirst(e)将指定元素添加到头部

offerLast(e)将指定元素添加到尾部

 

peekFirst()获取集合头部元素

peekLast()获取集合尾部元素

获取元素时不会删除元素,当集合为空时会返回null

 

poolFirst()移除集合头部元素

poolLast()移除集合尾部元素

获取元素,并且元素被删除,当集合为空时会返回null

 

七,集合的几个应用:

需求一:使用LinkedList模拟一个堆栈或者队列的数据结构;
     
堆栈:先进后出

     队列:先进先出

class DuiZhan
{
    private LinkedList lk;
    //堆栈一初始化就创建了一个LinkedList
    DuiZhan() {
        lk = new LinkedList();
    }
    public void myAdd(Object obj) {
        lk.addFirst(obj); //向LinkedList头部添加元素
    }
    public Object myGet() {
        return lk.removeFirst();//堆栈是后进的元素先出,所以获取头部元素并移除
    }
    public boolean myEmpty() {
        return lk.isEmpty(); //判断LinkedList是否为空,为空了则停止操作
    }
}
public class DuiZhanDemo
{
    public static void main(String[] agrs) {
        DuiZhan dz = new DuiZhan();
        dz.myAdd("java01");
        dz.myAdd("java02");
        dz.myAdd("java03");
        while(!dz.myEmpty()) {
            System.out.println(dz.myGet());
        }
    }
}

需求二:去除ArrayList中的重复的元素;

    分析:现实生活中,我们去除某一容器中重复元素的方法是:先挨个取出该容器中的元素,放入一个临时空的容器当中,在放入之前判断这个临时容器中是否存在存我取出的这个元素,如果存在就放入这个临时的容器。

public class ArrayListTest
{
    public static void main(String[] args) {
        ArrayList al = new ArrayList();
        al.add("java01");
        al.add("java02");
        al.add("java01");
        al.add("java03");
        al.add("java01");
        sop(al);//打印原集合
        sop(singleArrayList(al));//打印新集合
    }
    public static List singleArrayList(ArrayList ai) {
        //定义一个临时存储容器
        ArrayList al = new ArrayList();
        for (Iterator it = ai.iterator();it.hasNext() ; )
        {
            //迭代出原集合中的元素,存放到Object中
            Object obj = it.next();
            if(!al.contains(obj))//如果临时集合不包含该元素,则将该元素存储到临时集合
                al.add(obj);
        }
        return al;
    }
    public static void sop(Object obj) {
        System.out.println(obj);
    }
}

需求三:将自定义对象作为元素传入到ArrayList当中,去除重复元素。

比如:人有姓名,年龄,重复元素相当于姓名和年龄相同的人。

思路:

1,将人进行描述,将人的姓名,年龄封装到人的对象;

2,定义容器将人存入;

3,取出;

class Person
{
    private String name;
    private int age;
    Person(String name,int age) {
        this.name = name;
        this.age = age;
    }
    //重写人的equals方法,用于比较两个Person对象是否是同一个对象
    public boolean equals(Object o) {
        if (!(o instanceof Person) )
            return false;
        Person p = (Person)o;
        System.out.println(this.name + "----------"  + p.name);
        return this.name.equals(p.name) && this.age == p.age;
    }
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
}
public class ArrayListTest2
{
    public static void main(String[] args) {
        ArrayList li = new ArrayList();
        li.add(new Person("张三",20));
        li.add(new Person("王五",21));
        li.add(new Person("张三",20));
        li.add(new Person("李四",25));
        //去除相同元素
        li = singleArrayList(li);
        
        //迭代打印去除相同元素后的集合元素
        for (Iterator it = li.iterator();it.hasNext(); )
        {
            Person p = (Person)it.next();
            sop(p.getName() + "++++++++" + p.getAge());
        }
    }
    public static void sop(Object obj) {
        System.out.println(obj);
    }
    public static ArrayList singleArrayList(ArrayList at) {
        ArrayList am = new ArrayList();
        for (Iterator it = at.iterator();it.hasNext() ; )
        {
            Object obj = it.next();
            if (!am.contains(obj))//该contains方法是比较两个obj是否相等,
                                //obj是Person实例对象,判断两个Person对象是不是相等,要看equals方法的定义
                am.add(obj);
        }
        return am;
    }
}

八,Set--元素无序(取出和存入的顺序不一致),元素不可重复;

|--HashSet底层的数据结构是哈希表;

HashSet如何保证数据的唯一性呢?是通过元素的两个方法hashCode和equals方法;先判断hashCode值是否相同,当相同时才会判断equals是否为true;当hashCode值不同时不会判断equals方法;

Set集合的功能和Collection是一样的,Set接口的方法也是一样的,所以可以直接使用Set集合了,重点是Set集合的子类。

 

********重要结论:HashSet对于判断元素是否存在(contains),以及对元素的删除(remove),都依赖于hashCode和equals方法。这点不同于ArrayList,ArrayList在做这些操作的时候值依赖于equals方法。面试可能会提到的问题。

 

HashSet在判断元素是否重复的方法是判断地址值hashCode是否相同,当哈希值不相同的时候,就不再比较equals方法,就认为两个元素是不相同的。当哈希值相同的时候,才会比较equals方法,判断两个对象时候相等,相等则返回true。所以在用HashSet集合存储我们自定义对象的时候,比较两个HashSet集合的元素是否相等,需要重写hashCode()和equals()方法。

 

需求四:定义Person类,将Person对象存储在HashSet集合中,去除同一个人,将姓名和年龄都相同的人视为同一个人。思路和需求三相同。

public class HashSetDemo
{
    public static void main(String[] args) {
        HashSet hs = new HashSet();
/*      hs.add("java01");
        hs.add("java02");
        hs.add("java03");
        hs.add("java04");
        sop(hs.add("java01"));//打印的结果是Boolean型的原因是,这些方法判断的是是否可以存取,可以则为true
        //否则相反,因为set集合中的元素师不可以重复的,当重复时,equals方法会返回false
        sop(hs.add("java05"));
        
        for (Iterator it = hs.iterator();it.hasNext() ; )
        {
            sop(it.next());
        }
*/
    
        hs.add(new Person("a1",11));
        hs.add(new Person("a2",12));
        hs.add(new Person("a3",13));
        hs.add(new Person("a2",12));
        hs.add(new Person("a1",11));
        sop(hs.contains(new Person("a1",11)));//判断某个对象是否存在于集合中,
                                        //先判断这个对象的哈希值,相同,然后判断equals是否为true,remove方法是同理的
        
        for (Iterator it = hs.iterator();it.hasNext() ; )
        {
            Person p = (Person)it.next();
            sop(p.getName() + "::" + p.getAge());//如果不重写hashCode方法,打印则五个都能打印出来,因为它们五个
            //在对内存中的哈希值都不同,是不同的对象,默认的hashCode方法当哈希值不同时就不会比较equals方法了。
            //所以要想去除同一个人(姓名和年龄都相同视为同一个人),还要重写hashCode方法;
        }
    }
    public static void sop(Object obj) {
        System.out.println(obj);
    }
}
class Person
{
    private String name;
    private int age;
    Person(String name,int age) {
        this.name = name;
        this.age = age;
    }
    public int hashCode() {
        System.out.println(this.name + "HashCode");
        return name.hashCode() + age * 37; //这里也可以返回同一个hashCode值,原理是当哈希值相同时,会判断equals
    }
    public boolean equals(Object obj) {
        if(!(obj instanceof Person))
            return false;
        Person p = (Person)obj;
        //System.out.println(this.name + "----equals-----" + p.name);//判断时候调用了equals方法
        return this.name.equals(p.name) && this.age == p.age;
    }
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
}

九,TreeSet的应用

Set 无序,元素不可重复

|--HashSet  线程是不同步的。

保证数据的唯一性的原理是:先调用hashCode判断哈希值是否相同,如果相同则判断equals方法是否为真,为真则重复

如果哈希值不同则不再判断equals方法。

 

|--TreeSet   可以对Set集合中的元素进行排序;

底层数据结构是二叉树;

保证数据唯一性的依据是:compareTo方法返回0,与哈希值无关,只有哈希Set与哈希值有关;

不管是比较,删除还是其他操作,都是调用compareTo方法。

***TreeSet排序的第一种方式:让元素自身具备比较性。

元素需要实现Compareable接口,覆盖compareTo方法。

这种顺序也称为自然顺序,或者叫做默认顺序。

*** TreeSet排序的第二种方式:

当元素自身不具备比较性,或者具备的比较性不是所需要的;

这是就需要让集合自身具备比较性。

在集合初始化时就具备了比较方式。

 

使用第一种比较方式,请参考下面这个示例:

需求:将学生对象存储到集合TreeSet中按照年龄进行排序;

记住:进行排序的时候,当主要条件相同时一定要判断次要条件;

public class TreeSetDemo
{
    public static void main(String[] args) {
        TreeSet ts = new TreeSet();
    /*  ts.add("ahskjdh");
        ts.add("aaa");
        ts.add("DCjhk");
        */
        ts.add(new Student("lisi009",15));
        ts.add(new Student("lisi005",15));
        ts.add(new Student("lisi008",19));
        ts.add(new Student("lisi007",13));
        for (Iterator it = ts.iterator();it.hasNext() ; )
        {
            Student s = (Student)it.next();
            sop(s.getName() + "::" + s.getAge());
        }
    }
    public static void sop(Object obj) {
        System.out.println(obj);
    }
}
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;
        if (this.age > stu.age)
            return 1;
        //当年龄相等时,还要比较姓名是不是相同,才能比较这两个元素是不是重复元素
        //如果年龄相等就返回0,那么TreeSet就会把年龄相同的两个元素视为同一元素。
        if (this.age == stu.age)
        {
            return this.name.compareTo(stu.name);//当主要条件相同时要判断次要条件,如果不判断次要条件,就会出现
                                                //姓名不同,但是年龄相同的人被识别为同一个人(重复元素)被删除掉。
                                                //这里的compareTo方法是String类的,因为String类实现了compareTo方法。
        }
        return -1;
    }
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
}

扩展知识点:如果想让TreeSet集合的输出顺序按照存储顺序输出,这是只要将compareTo方法的返回值设置为正数,就可以按照存入顺序输出。

如果将返回值设置为负数,那么输出顺序就是按照存储顺序的反向输出。如果将返回值设置为0,那么集合中就只有一个元素存活。这些是源于二叉树的

原理,进行的。

 

当元素自身不具备比较性,使用第二种比较方式对TreeSet集合的元素进行比较,是在TreeSet集合一初始化就给集合一个比较器,让集合自身具备比较方式:

这种方式使用的起来比实现compareTo方法更常用,使用起来也更加好。

 

方法:定义一个类实现Comparator接口,在Comparator接口中覆盖compare方法。

方式二比较的示例:

需求:将学生对象,存储到TreeSet集合中,通过比较姓名的方式进行排序。

public class TreeSetDemo2
{
    public static void main(String[] agrs) {
        TreeSet ts = new TreeSet(new myCompare());
        ts.add(new Student2("lisi009",15));
        ts.add(new Student2("lisi005",15));
        ts.add(new Student2("lisi008",19));
        ts.add(new Student2("lisi007",13));
        ts.add(new Student2("lisi007",15));
        ts.add(new Student2("lisi007",13));
        for (Iterator it = ts.iterator();it.hasNext() ; )
        {
            Student2 s = (Student2)it.next();
            sop(s.getName() + "::" + s.getAge());
        }
    }
    public static void sop(Object obj) {
        System.out.println(obj);
    }
}
class Student2
{
    private String name;
    private int age;
    Student2(String name,int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    } 
}
class myCompare implements Comparator
{
    public int compare(Object obj1,Object obj2) {
        Student2 s1 = (Student2)obj1;
        Student2 s2 = (Student2)obj2;
        //return s1.getName().compareTo(s2.getName());
        //这种方式比较的只是姓名,是以姓名的为依据,当姓名相同,年龄不同时会被视为同一个人,不能被打印出来。
        //所以还应增加比较年龄的方法。
        int num =  s1.getName().compareTo(s2.getName());
        if (num == 0)
        {/*
            if(s1.getAge() > s2.getAge())
                return 1;
            if(s1.getAge() == s2.getAge())
                return 0;
            return -1;  */
            return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
            //因为在Integer中也封装了compareTo方法,比较方法和以上注释一样。
        }
        return num;
    }
}

一个TreeSet集合使用的经典小例子:

需求:按照字符串的长度将字符串存储到TreeSet集合中,注意:当字符串长度相同时,还要判断此字符串是否是同一个字符串,如果不是不能删除这个字符串,要将其也存储到TreeSet集合中。

分析:字符串自身具备比较性,但是默认的比较性不是我们所需求的。字符串默认的是使用自然顺序进行排序的。这时就只能使用比较器。

import java.util.*;
public class TreeSetExe
{
    public static void main(String[] args) {
        TreeSet ts = new TreeSet(new CompareLenTreeSet());
        ts.add("afsg");
        ts.add("afsgrh");
        ts.add("afsgasgh");
        ts.add("afsg");
        ts.add("aaaa");
        for (Iterator it = ts.iterator();it.hasNext() ; )
        {
            sop(it.next());
        }
    }
    public static void sop(Object obj) {
        System.out.println(obj);
    }
}
class CompareLenTreeSet implements Comparator
{
    public int compare(Object o1,Object o2) {
        String s1 = (String)o1;
        String s2 = (String)o2;
    /*  if (s1.length() > s2.length())
            return 1;
        if(s1.length() == s2.length())
        //  return 0; //这里如果只返回0,则比较的仅仅是主要条件,当比较长度相同的不同字符串时,程序只会依据长短判断
        //就会删除这类字符串。所以当主要条件满足时还应判断次要条件。
        return s1.compareTo(s2);
        return -1;
    */
        int num = new Integer(s1.length()).compareTo(s2.length());
        if(num==0)
            return s1.compareTo(s2);//当字符串长度相同时,采用自然比较法,比较字符串的大小。
        return num;
    }
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值