第十七天

it2025-03-03  18

集合技术

作业

猜数字游戏

/*

* 猜数字游戏

*/

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("您猜大了,请继续");

            }

        }

    }

}

 

Collection集合回顾

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

增:add、addAll

删除:clear、remove、removeAll、RetainAll

查询:size

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

判断:contains、containsAll、isEmpty

    

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

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

细节:

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

 

List接口:

Set接口:

List接口

List接口特点

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

有序 可以重复 有下标

 

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

    add(int index , Object element )

    remove( int index )

    get( int index )

    set( int index , Object element )

    

List接口自己的迭代器:

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

ArrayList集合

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

LinkedList集合

LinkedList介绍:

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

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

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

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

        

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

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

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

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

    

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

        }

    }

模拟数据结构

    /*

     * 由于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

         */    

    }

Vector集合

Vector集合介绍

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

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

 

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

        }

    }

 

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

        }

    }

 

List接口总结:

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

 

ArrayList:

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

LinkedList:

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

Vector:

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

 

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

Set接口

Set接口介绍

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

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

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

 

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

HashSet集合(重点

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

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

    HashSet:

    TreeSet:

HashSet集合介绍

 

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

 

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

        }        

    }

}

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

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

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

 

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

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

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

 

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

 

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

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个对象是否相同。

HashSet保证对象唯一原因

HashSet集合保证对象唯一:

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

 

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

HashSet总结

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

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

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

5、HashSet不安全。

LinkedHashSet介绍

 

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

它的特点:

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

 

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

        }

 

    }

}

TreeSet集合

TreeSet介绍

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

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

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

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

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

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

 

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

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

        }

    }

}

树结构介绍

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

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

 

 

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

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

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

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

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;

        

    }

}

Comparable接口

 

 

 

 

Comparator接口

 

 

 

 

 

Collection接口下的实现类总结

 

 

 

 

 

 

Map集合

Map集合引入

 

 

 

 

Map集合方法

添加方法

 

 

 

删除方法

 

 

 

查询方法

 

 

 

 

 

 

keySet方法

 

 

 

entrySet方法

 

 

 

 

values方法

 

 

 

HashMap集合

 

 

 

 

 

TreeMap集合

 

 

 

 

集合练习

练习需求:

学生管理系统程序。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/beyondcj/p/6270769.html

最新回复(0)