第十七天

集合技术

  1. 作业

    1. 猜数字游戏

/*

* 猜数字游戏

*/

public class HomeWork1 {

    public static void main(String[] args) {

        // 获取被猜的那个数字,需要使用随机数类产生

        Random r = new Random();

        // 获取到1~100之间的一个数字

        int number = r.nextInt(100) + 1;

        // 创建专门负责键盘录入的那个对象

        Scanner sc = new Scanner( System.in );

        // 使用死循环,完成数字的判断和重复输入功能

        while( true ){

            // 获取键盘录入的数字

            System.out.println("请输入您猜的数字(范围1~100):");

            int x = sc.nextInt();

            // 判断当前输入的数字和被猜的那个数字关系

            if( x == number ){

                System.out.println("恭喜您,猜中啦!!!");

                break;

            }else if( x < number ){

                System.out.println("您猜小了,请继续");

            }else{

                System.out.println("您猜大了,请继续");

            }

        }

    }

}

 

  1. Collection集合回顾

集合:Collection接口,它是集合的顶层接口。其中定义了集合共性的操作方法。

增:add、addAll

删除:clear、remove、removeAll、RetainAll

查询:size

遍历:iterator,得到一个迭代器对象

判断:contains、containsAll、isEmpty

    

    迭代器对象:Iterator,它是所有集合共有的迭代对象

  1. 先要判断,使用hasNext方法
  2. 取出元素,使用next方法

细节:

  1. 一次判断,最好使用一次next方法
  2. 在遍历的时候,不要使用集合自身的增删方法修改集合。

 

List接口:

Set接口:

  1. List接口

    1. List接口特点

List接口它是Collection接口的子接口。List接口下的所有集合容器:

  1. 有序
  2. 可以重复
  3. 有下标

 

由于List接口下的集合拥有下标,因此List接口拥有自己特有的方法:这些方法都是围绕下标设计的。

    add(int index , Object element )

    remove( int index )

    get( int index )

    set( int index , Object element )

    

List接口自己的迭代器:

    ListIterator:它可以正向或逆向遍历List集合。同时可以对集合进行增,删、改、查操作。

  1. ArrayList集合

ArrayList:它的底层是可变数组,查询快,增删慢,不安全!

  1. LinkedList集合

    1. LinkedList介绍:

LinkedList集合,它也List接口的实现类。和ArrayList相同。都可以去使用List接口中的所有方法。

sun公司给List接口提供多个实现类的目的:

    原因是实际开发中,我们需要不同的容器来存储不同对象。

不同的容器:每个容器都有自己对数据的存储方式(数据结构)。不同方式结构存储的数据,它们在性能上差异很大。

        

        LinkedList集合:它的底层是链接列表结构(链表)。

  1. LinkedList数据结构(理解链表结构):

链表:它主要也是用来存储数据。存储数据的每个空间被称为节点。

节点一般分成2个小空间:一个存储的节点的地址,一个存储的真正存放的数据。

    

  1. LinkedList结构演示(编写代码测试)

由于LinkedList集合底层是链表结构。因此LinkedList集合在List接口之上,有增加了围绕头和尾而设计的增、删、改、查操作。

xxxxFirst 和 xxxxxLast方法

    // 删除方法

    public static void demo2() {

        /// 创建集合对象

        LinkedList list = new LinkedList();

        

        // 添加元素

        list.addFirst("aaa");

        list.addFirst("bbb");

        list.addLast("ccc");

        

        // 删除方法

        Object obj = list.removeFirst();

        System.out.println(obj);

        

        System.out.println(list);

        

    }

 

    // 添加方法

    public static void demo1() {

        /// 创建集合对象

        LinkedList list = new LinkedList();

        

        // 添加元素

        list.addFirst("aaa");

        list.addFirst("bbb");

        list.addLast("ccc");

        

        // 遍历

        for( Iterator it = list.iterator() ; it.hasNext() ; ){

            System.out.println(it.next());

        }

    }

  1. 模拟数据结构

    /*

     * 由于LinkedList集合它有头和尾,因此经常使用这个集合模拟其他的数据结构

     * 队列结构:这种结构存储的数据在容器,最先进入容器的元素,先先出去。

     *     简单介绍:先进先出,后进后出

     *     例如:排队买票。火车过山洞。

     *

     * 堆栈结构:这种结构存储的数据在容器,最先进入的最后出去

     *     简单介绍:先进后出,后进先出。

     *     例如:弹夹。Java中的栈内存。

     */

    public static void demo3() {

        

        /// 创建集合对象

        LinkedList list = new LinkedList();        

        

        // 添加元素

        list.addLast("aaa");

        list.addLast("bbb");

        list.addLast("ccc");

        list.addLast("ddd");

        list.addLast("eee");

        

        // 模拟队列结构

        System.out.println(list.removeFirst());

        System.out.println(list.removeFirst());

        System.out.println(list.removeFirst());

        System.out.println(list.removeFirst());

        System.out.println(list.removeFirst());

        

        /*

         * 结论:使用LinkedList模拟队列结构的时候:

         *     添加和删除的方法调用正好相反。

         *     添加使用addLast ,删除就使用removeFirst

         *     添加使用addFirst,删除就使用removeLast

         *     

         * 模拟堆栈结构:

         * 添加使用addLast ,删除就使用removeLast

         * 添加使用addFirst ,删除就使用removeFirst

         */    

    }

  1. Vector集合

    1. Vector集合介绍

Vector集合它是JDK1.0时期存在的集合。其功能和ArrayList集合相同。

Vector的底层使用的也是可变数组。Vector集合它增删、查询都慢。它的底层是安全的。后期看到Vector集合,就当作ArrayList集合使用。

 

  1. Vector集合演示

    // 使用Iterator遍历

    public static void demo1() {

        

        // 创建集合对象

        Vector v = new Vector();

        

        // 添加方法

        v.addElement("aaa");

        v.add("bbb");

        v.add("bbb");

        v.add("ccc");

        

        // 使用Iterator遍历

        for( Iterator it = v.iterator() ; it.hasNext() ; ){

            System.out.println(it.next());

        }

    }

 

  1. Enumeration接口(知道这个接口作用)

    // 使用古老的枚举迭代器遍历

    public static void demo2() {

        // 创建集合对象

        Vector v = new Vector();

        

        // 添加方法

        v.addElement("aaa");

        v.add("bbb");

        v.add("bbb");

        v.add("ccc");

        

        /*

         * 使用Vector中的 elements 方法可以得到一个枚举迭代器(早期迭代器)

         * Enumeration : 它是一个接口,主要用来遍历集合(Vector)

         * Enumeration这个接口被Iterator代替,并且Iterator中有remove方法,

         *     Iterator中的方法名称较短。

         */

        Enumeration en = v.elements();

        while( en.hasMoreElements() ){

            System.out.println(en.nextElement());

        }

    }

 

  1. List接口总结:

List接口:它限定它下面的所有集合容器拥有下标、可以存放重复元素、有序。其中定义了围绕下标而操作的方法。

 

ArrayList:

    底层是可变数组。增删慢、查询快。不安全。可以使用null作为元素。

LinkedList:

    底层是链表结构。增删快、查询慢,不安全。可以使用null作为元素。其中定义了围绕头和尾的方法,可以模拟 队列或堆栈数据结构。

Vector:

    底层是可变数组,被ArrayList代替。什么都慢。但安全。可以使用null作为元素

 

Enumeration:它是古老的迭代器。被Iterator代替。

  1. Set接口

    1. Set接口介绍

前面学习Collection接口的时候,下面有2个子接口:

    List接口:可以保存重复元素,有下标,有序。

    Set接口:可以保存不重复元素。

 

    注意:Set接口没有自己特有的方法,所有方法全部来自于Collection接口。

  1. HashSet集合(重点

Set接口下的所有集合容器中保存的元素都不会重复。

Set接口下有2个重要的集合:

    HashSet:

    TreeSet:

  1. HashSet集合介绍

 

HashSet:它的底层是哈希表结构支持。它不保证迭代顺序(存取),同时它不安全。

 

  1. HashSet演示

/*

* 演示 HashSet集合

*/

public class HashSetDemo {

    public static void main(String[] args) {

        

        // 创建集合对象

        HashSet set = new HashSet();

        

        // 添加元素

        set.add("aaa");

        set.add("aaa");

        set.add("bbb");

        set.add("ccc");

        set.add("ccc");

        set.add("ddd");

        

        // 使用Iterator遍历

        for( Iterator it = set.iterator() ; it.hasNext() ; ){

            System.out.println( it.next() );

        }        

    }

}

  1. 哈希表介绍(理解哈希表存储元素方式)

哈希表:它也是一种存储数据的方式。它的底层使用的依然是数组,只是在存储元素的时候不按照数组下标从小到大的顺序存放。

如果有元素需要给哈希表结构中保存的时候,这时不会直接将元素给表中保存,而是根据当前这个元素自身的一些特点(成员变量等)计算这个元素应该在表中的那个空间中保存。

 

哈希表存放对象的时候,需要根据当前对象的特定计算对象在表中的存储位置。任何对象都可以给集合中保存,那么任何对象肯定可以给HashSet集合中保存。

任何对象在保存的时候,都需要计算存储位置。任何对都应该具体计算存储位置的功能,这个功能(方法)定义在Object类中。

我们给任何哈希表中存储的对象,都会依赖这个对象的hashCode方法计算哈希值,通过哈希值确定对象在表中的存储位置。

 

哈希冲突:如果两个对象调用hahsCode方法之后得到的哈希值相同,称为哈希冲突。

 

在给哈希中存放对象的时候,如果存储哈希冲突,这时就会调用2个对象equals方法计算它们是否相同。如果相同,就丢弃当前正要存放的这个对象,如果不同就会继续保存。

  1. HashSet存放自定义对象(必须书写代码测试,理解hashCode和equals方法作用)

自定义对象:不使用JDK中提供的类创建的对象,自己书写一个,然后创建这个类的对象,最后将其保存在HashSet集合中。

 

/*

* 演示给HashSet中存放自定义对象

*/

public class HashSetDemo2 {

    public static void main(String[] args) {

        

        // 创建集合对象

        HashSet set = new HashSet();

        

        // 添加Person对象到集合中

        Person p = new Person("zhangsan",12);

        set.add(p);

        set.add(new Person("lisi",22));

        set.add(new Person("lisi",22));

        set.add(new Person("wangwu",29));

        set.add(new Person("zhaoliu",32));

        set.add(new Person("zhaoliu",32));

        set.add(new Person("tianqi",35));

        

        // 遍历

        for( Iterator it = set.iterator(); it.hasNext() ; ){

            System.out.println(it.next());

        }

        

        

    }

}

 

 

/*

* 自定义类

*/

public class Person {

    private String name;

    private int age;

    // alt + shift + S

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    public int getAge() {

        return age;

    }

    public void setAge(int age) {

        this.age = age;

    }

    @Override

    public String toString() {

        return "Person [name=" + name + ", age=" + age + "]";

    }

    public Person(String name, int age) {

        super();

        this.name = name;

        this.age = age;

    }

}

运行上面的程序,给HashSet中保存Person对象:

发现运行的结果出现了重复的数据。

 

解释存储的Person对象重复的原因:

    我们知道HashSet的底层使用的哈希表,哈希表在存储对象的时候需要调用对象自身的hashCode方法计算在表中存储的位置。

    而在我们自己的程序中,我们创建了多个Person对象,每个Person对象都在堆中有自己的内存地址。虽然有些Person对象表示的name和age相同的,但是他们所在的对象的内存地址是不同的。而我们在书写的Person类又继承了Object类,这样就相当于Person类拥有了hashCode方法,而这个hashCode方法完全使用的是Object类中的。而Object类中的hashCode方法是根据当前对象的内存地址计算哈希值,每个Person对象都拥有自己的内存地址,即使他们的name和age相同,但是他们的内存地址不同,计算出来的哈希值肯定也不同,那么每个Person对象都可以保存到哈希表中。

 

解决方案:

    根据每个Person对象自己的name和age计算它们的哈希值。相当于Person类继承到Object类中的hashCode方法不适合当前Person类,Person类需要复写hashCode方法。

    复写完hashCode方法之后,还要复写Object类中的equals方法。因为如果hashCode计算的结果相同,这时还要调用equals方法来判断2个对象是否相同。

    而Object类中的equals方法在使用2个对象的地址比较。而我们创建的每个Person地址都不相同,那么直接使用Object类中的equals方法,比较的结果肯定是false。我们希望通过2个对象的name和age比较2个对象是否相同。

  1. HashSet保证对象唯一原因

HashSet集合保证对象唯一:

  1. 首先会调用对象的hashCode方法计算存储位置。
    1. 如果位置相同,会调用equals方法。如果equals方法返回的true,当前对象就不会被保存。如果是false依然保存。

 

结论:以后只要是给HashSet集合中保存的对象,这个对象所属的类一定要复写Object类中的hashCode和equals方法。

  1. HashSet总结

1、HashSet集合是Set接口的实现类。它保证元素不重复。
2、HashSet底层使用的哈希表,不保证存取的顺序(迭代顺序)。

3、保证对象不重复需要复写hashCode和equals方法。

4、HashSet集合只能使用Iterator和foreach遍历,不能使用List接口的特有方法遍历。

5、HashSet不安全。

  1. LinkedHashSet介绍

 

LinkedHashSet集合:它是HashSet的子类。它的底层接口是链表+哈希表结构。

它的特点:

  1. 肯定可以保证对象唯一
  2. 不安全
  3. 可以保证元素的存取顺序。

 

LinkedHashSet集合没有自己特有方法,所有方法全部继承与HashSet集合。

 

/*

* 演示LinkedHashSet集合

*/

public class LinkedHashSetDemo {

    public static void main(String[] args) {

 

        // 创建集合对象

        LinkedHashSet set = new LinkedHashSet();

 

        // 添加元素

        set.add("ccc");

        set.add("bbb");

        set.add("bbb");

        set.add("ddd");

        set.add("ddd");

        set.add("aaa");

        set.add("aaa");

 

        // 使用Iterator遍历

        for (Iterator it = set.iterator(); it.hasNext();) {

            System.out.println(it.next());

        }

 

    }

}

  1. TreeSet集合

    1. TreeSet介绍

ArrayList:底层可变数组,可以保存重复元素,有下标。

LinkedList:底层链表结构,有头和尾,可以保存重复元素。

HashSet:底层哈希表,不重复,不保证存储顺序。

LinkedHashSet:底层哈希表+链表,不重复,保证存取顺序。

上面的这些集合容器可以存储对象,但是他们都不能对其中保存的对象进行排序操作。

TreeSet:它依然是Set接口的实现类,肯定可以使用Set接口中的所有方法。同时也会保证对象的唯一。TreeSet集合容器中保存对象的时候,只要将对象放到这个集合中,集合底层会自动的对其中的所有元素进行排序。当我们在取出的时候,元素全部排序完成。

 

TreeSet集合:它的底层使用二叉树(红黑树)结构。这种结构可以对其中的元素进行自然排序。

  1. TreeSet集合演示

/*

* 演示TreeSet集合

*/

public class TreeSetDemo {

    public static void main(String[] args) {

        

        // 创建集合对象

        TreeSet set = new TreeSet();

        // 给集合中添加方法

        set.add("aaa");

        set.add("aaa");

        set.add("aaa");

        set.add("bbb");

        set.add("bbb");

        set.add("AAA");

        set.add("ABC");

        set.add("Abc");

        set.add("123");

        // 遍历

        for( Iterator it = set.iterator() ; it.hasNext() ; ){

            System.out.println(it.next());

        }

    }

}

  1. 树结构介绍

树:它也是一种数据结构。这种结构它默认可以对其中的数据进行排序。

我们如果给树结构中存储元素的时候,每个存储元素的空间被节点。处于树根的位置节点称为根节点。其他节点称为子节点(叶子节点)。

 

 

TreeSet底层存储数据时的方式:

    当我们给TreeSet集合中保存对象的时候,需要拿当前这个对象和已经在树上的元素进行比较大小,如果存储的元素小,就会当前这个节点的左侧保存,如果比当前这个节点的值大,就给当前这个节点的右侧保存。如果和当前 这个节点相同,丢弃不保存。

    需要拿当前这个对象和当前节点上的值进行大小的比较。这时要求能够TreeSet集合中保存的对象,一定可以进行大小的比较。

注意:给TreeSet中保存的对象,一定要保证这些对象类型相同,或者他们之间有继承关系。

  1. TreeSet集合保存自定义

 

上面代码在运行的时候发生异常:

 

    发生异常的原因:

        在TreeSet的底层,需要将添加到TreeSet集合中的对象强制转成Comparable类型。如果添加的对象不属于Comparable类型,那么在添加的时候就会发生类型转换异常。

    底层将传递的对象强转成Comparable接口的原因:因为Comparable接口是Java中规定的比较大小的接口。只要哪个类需要比较大小,就应该主动去实现Comparable接口。

    

    异常的解决方案:让Person类实现Comparable接口。

 

/*

* 自定义类

*/

public class Person implements Comparable{

    private String name;

    private int age;

    // alt + shift + S

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    public int getAge() {

        return age;

    }

    public void setAge(int age) {

        this.age = age;

    }

    @Override

    public String toString() {

        return "Person [name=" + name + ", age=" + age + "]";

    }

    public Person(String name, int age) {

        super();

        this.name = name;

        this.age = age;

    }

    /*

     * 当一个类实现Comparable接口之后,这个类中肯定会compareTo方法

     * 而这个方法才是真比较对象大小的方法,

     * 这个方法的返回值有三种情况:

     * 零:表示两个对象相同

     * 正数:调用这个方法的那个对象,比传递进来的这个对象大

     * 负数:调用这个方法的那个对象,比传递进来的这个对象小

     * 因此一般要求在实现Comparable接口之后在compareTo方法中

     * 根据当前对象的自身属性的特定比较大小

     */

    public int compareTo(Object o) {

        // 由于传递进来的对象被提升成Object类型,因此需要向下转型

        if( !(o instanceof Person ) ){

            // 如果判断成立,说明传递进来的不是Person

            throw new ClassCastException("请传递一个Person进来,否则不给你比较");

        }

        // 向下转型

        Person p = (Person) o;

        // 因为age都是int值,如果相等,它们的差值恰好是零

        int temp = this.age - p.age;

        return temp == 0 ? this.name.compareTo(p.name) : temp;

        

    }

}

  1. Comparable接口

 

 

 

 

  1. Comparator接口

 

 

 

 

 

  1. Collection接口下的实现类总结

 

 

 

 

 

 

  1. Map集合

    1. Map集合引入

 

 

 

 

  1. Map集合方法

    1. 添加方法

 

 

 

  1. 删除方法

 

 

 

  1. 查询方法

 

 

 

 

 

 

  1. keySet方法

 

 

 

  1. entrySet方法

 

 

 

 

  1. values方法

 

 

 

  1. HashMap集合

 

 

 

 

 

  1. TreeMap集合

 

 

 

 

  1. 集合练习

    1. 练习需求:

学生管理系统程序。

 

 

 

  1.  

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2017-01-10 20:56  beyondcj  阅读(500)  评论(0编辑  收藏  举报