zoukankan      html  css  js  c++  java
  • java基础第十三篇之Collection

    常见的几种数据结构:
     * 1.堆栈:先进后出
     * 2.队列:先进先出
     * 3.数组:查找快,增删慢
     * 4.链表:查找慢,增删快
     
     import java.util.LinkedList;

    /*
     * java集合的根接口 Collection
     *         共性的方法:增删改查
     *         增:add(E e);//addAll(Collection<? extends E> c)
     *         删:remove(Object obj);
     *         改:无 
     *         查:size()
     *         其他:toArray();clear();isEmpty();
     *  
     * 分为两个子接口:List<E>,Set<E>
     * 1.List:接口,有实现类:ArrayList<E>,LinkedList<E>,Vector<E>
     *         特点:a.有序的,(这里的有序 不是指123,或者abc这种自然顺序,指的存和取的顺序一致)  
     *             b.有索引(有下标)
     *             c.元素可重复
     *         List接口中定义方法:增删改查
     *             增加: add(E e);add(int index,E e);//在指定下标处增加一个元素
     *             删除: remove(Object obj); remove(int index);//删除指定下标元素
     *             修改: set(int index,E e);//把指定下标元素改为新的元素
     *             查询: get(int index);//获取指定下标的元素
     *         List接口下各种实现类的数据结构特点:
     *             ArrayList:    内部采用动态数组结构:查找快,增删慢
     *             LinkedList: 内部采用双向链表结构:查找慢,增删快
     *             Vector:     内部采用数组结构:查找快,增删慢
     *             说明:Vector是从JDK1.0 开始有的
     *                 Collection集合根接口从JDK1.2开始的
     *             ===============================================
     *             实际上ArrayList和Vector中基本没有特有的方法,这些实现类的方法基本上都是List中的方法
     *             LinkedList特有的方法:    
     *             第一组:
     *             addFirst(E e),addLast(E e);
     *             第二组:
     *             public E removeFirst();//返回并删除首元素
     *             public E removeLast();
     *             第三组:
     *             public E getFirst();//返回首元素
     *             public E getLast();
     *             第四组:
     *             public E pop();//出栈,弹栈,就是从集合中删除元素(哪一个:集合首个元素,类似于 removeFirst)
     *             public void push(E e);//压栈,就是向集合添加一个元素(添加到集合的第一个,类似于add,addFirst)
     *             
     *                 
     * 2.Set:接口,有实现类,HashSet,LinkedHashSet,TreeSet
     *         特点:a.元素不重复
     *             b.无索引(无下标)
     *             c.无序(有例外,LinkedHashSet,TreeSet)
     *         Set接口中定义方法:增删改查(特别好记,Set接口中没有特有方法完全和Collection接口一模一样)
     *             
     *         Set接口下各种实现类的数据结构特点:
     *             HashSet: 内部采用哈希表结构
     *                     特点:查找较快,增删也较快
     *             LinkedHashSet: 内部采用 链表结构+哈希表结构 组合结构
     *                     特点:查找较快,增删也较快
     *             HashSet和LinkedHashSet特有的方法:无
     *                 全部都是和Set接口中一样,和Collection接口中一样
     */
    public class LinkedListDemo01 {

        public static void main(String[] args) {
            // TODO Auto-generated method stub
            demo01();
        }
        /*
         * LinkedList特有的方法
         */
        public static void demo01(){
            //创建一个对象
            LinkedList<String> names = new LinkedList<String>();
            names.add("jack");
            names.add("rose");
            names.add("lilei");
            names.add("rose");
            //添加
    //        names.addFirst("hanmeimei");
    //        names.addLast("Tom");
            //获取(仅仅取出元素,但是不删除)
    //        String s1 = names.getFirst();
    //        System.out.println(s1);
    //        String s2 = names.getLast();
    //        System.out.println(s2);
            //删除元素(不仅取出元素,而且会在集合中删除)
    //        String s1 = names.removeFirst();
    //        System.out.println(s1);
    //        String s2 = names.removeLast();
    //        System.out.println(s2);
            //pop 出栈方法
    //         String s1 = names.pop();
    //         System.out.println(s1);
    //         String s2 = names.pop();
    //         System.out.println(s2);
            //push 入栈方法
            names.push("Tom");
            names.push("Jirui");
            System.out.println(names);
        }
    }


    *
     * HashSet 判断元素是否重复是根据两个条件
     * 
     * 1.看新元素的哈希值 和 所有旧元素是否相同,如果不相同那么不重复,存储
     * 
     * 2.如果哈希值相同了,调用equals方法,拿新元素和哈希值相同的那个旧元素,返回值true,那么判断重复元素,不存储
     *                             返回值false 判断不重复 可以存储
     * 
     * 
     * 哈希表:底层是 数组结构+链表结构  特点:查找较快,增删较快        哈希表又称散列表,是一种能将关键字映射成存储地址的记录存储技术。
        哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,
        以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。Hash就是找到一种数据内容和数据存放地址之间的映射关系。
     
     * 对象的哈希值:
     *         实际上java中所有的对象 都有一个字符串表示:toString(),默认:包名.类名@哈希值
     *         实际上java中所有的对象 都有一个数字的表示:hashCode();//返回的就是一个数字,就是我们说对象的哈希值
     * 
     * 
     * 字符串的哈希值:
     *         字符串类的哈希值 自己重写了hashCode,计算哈希值只跟内容有关系,所有内容相同的字符串,哈希值必定相同
     *                                 内容不同的字符串,哈希值还有可能相同
     *         
     * 
     * 
     */
    public class StringHashDemo01 {

        public static void main(String[] args) {
            // TODO Auto-generated method stub
            String s1 = new String("重地");
            String s2 = new String("通话");
            
            int h1 = s1.hashCode();
            int h2 = s2.hashCode();
            
            //System.out.println(h1==h2);
            //哈希值是根据地址值计算的,如果String类没有重写hashCode那么算出来的哈希值应该是不一样
            //因为String类重写了 hashCode,所以不同的地址和hash值的计算已经没有关系
            //System.out.println(s1==s2);//false
            System.out.println(h1);//96354
            System.out.println(h2);
    //        System.out.println(s1);
    //        System.out.println(s2);
            //我们看了String类的hashCode方法,字符串的哈希值,只跟内容有关系
            //可能不可能出现 字符串的内容不同 但是哈希值相同
            //比如:"abc"和"acD"
            //比如: "重地"和 "通话"
            
            
        }
        
        /*
         * 对象的哈希值
         */
        public static void demo01(){
            //创建Person对象
            Person p1 = new Person();
            int hash =  p1.hashCode();
            //hash就是p1的哈希值
            System.out.println(hash);
            System.out.println(p1.toString());
            //我们骗了19天,打印对象默认打印出来的不是地址值 是hash值
            //哈希值是根据地址值算出来的,怎么算的我们不知道
            //实际上 由地址值 计算哈希值 由一个算法计算 散列算法---哈希算法
            //在我们java程序中能不能打印出对象 真正的地址值(不能)
            //但是地址值 是真正存在的  对象中存储的就是真正的地址值 但是打印的时候打印出来是哈希值
            Person p2 = new Person();
            int hash2 = p2.hashCode();
            System.out.println(hash2);
            //不同的对象 地址值肯定不一样,但是哈希值有可能一样吗?
                    
        }

    }


    /*
     * 使用HashSet存储自定义的元素:存储Student对象
     * 
     * 我们要求:如果一个学生对象的年龄和姓名都相同,我们不让HashSet存储它
     * 
     * 以后写一个类:
     *         1.封装:
     *         2.构造(全参和无参)
     *         3.toString(方便输出对象)
     *         如果这个类还要存储到集合中最好
     *         4.hashCode
     *         5.euqals
     * 
     */
     
         public class Student {
            private int age;
            private String name;
            public int getAge() {
                return age;
            }
            public void setAge(int age) {
                this.age = age;
            }
            public String getName() {
                return name;
            }
            public void setName(String name) {
                this.name = name;
            }
            public Student() {
                super();
                // TODO Auto-generated constructor stub
            }
            public Student(int age, String name) {
                super();
                this.age = age;
                this.name = name;
            }
            @Override
            public String toString() {
                return "Student [age=" + age + ", name=" + name + "]";
            }
            @Override
            public int hashCode() {
                final int prime = 31;
                int result = 1;
                result = prime * result + age;
                result = prime * result + ((name == null) ? 0 : name.hashCode());
                return result;
            }
            @Override
            public boolean equals(Object obj) {
                if (this == obj)
                    return true;
                if (obj == null)
                    return false;
                if (getClass() != obj.getClass())
                    return false;
                Student other = (Student) obj;
                if (age != other.age)
                    return false;
                if (name == null) {
                    if (other.name != null)
                        return false;
                } else if (!name.equals(other.name))
                    return false;
                return true;
            }

        
        //自己重写hachCode
        // 20 abc
        // 20 acD
        // 19 acE
    //    @Override
    //    public int hashCode() {
    //        //我们目的,让年龄相同并且名字相同的对象返回的哈希值也相同
    //        return this.age * 31 + this.name.hashCode()*49;
    //    }
        //Eclipse自动生成的hashCode
        
        //自己重写equals方法,让年龄相同,名字相同两个对象调用equals返回值是true
    //    @Override
    //    public boolean equals(Object obj) {
    //        // TODO Auto-generated method stub
    //        Student s = (Student)obj;
    //        if(this.age != s.getAge()){
    //            return false;
    //        }
    //        if(!this.name.equals(s.getName())){
    //            return false;
    //        }
    //        return true;
    //    }
        //Eclipse自动生成的equals方法
        
        
        
        
        
        
    }

    /*
    * LinkedHashSet和HashSet的不同点:
     * HashSet是无序的
     * LinkedHashSet是有序的
     *         
     * contains方法和add方法的原理
     * 
     * 1.如果是ArrayList存储自定义元素 ,那么这个自定义类型 只要重写 equals
     * 
     * 2.如果是HashSet存储自定义元素,那么这个自定义类型 必须重写两个 hashCode,equals
     * 
     */

     /*
      * Collection<E>接口:
     * add(E e) remove(Object o),size(),toArray();isEmpty();contains()
     * 迭代器
     *     获取迭代器: Iterator<E> it =  集合对象.iterator();
     *     使用迭代器:
     *             it.hasNext(); it.next();
     * 
     *    List<E>和Set<E>
     *    能够说出List集合特点:有序,索引和重复
     *    能够说出Set集合的特点:无索引,不重复,无序的(但是LinkedHashSet,TreeSet是有序的)
     *
     *    使用List存储的数据结构:ArrayList是数组结构 LinkedList是链表结构
     *
     *    说出哈希表的特点:数组结构+链表结构: 查找较快,增删较快
     *        
     *    使用HashSet集合存储自定义元素(保证元素的内容相同不能存储)
     *        快捷键:alt+shift+s,h
     *        
     *    说出判断集合元素唯一的原理
     *        Collection中有contains(Object o);
     *        ArrayList中 只调用equals比较
     *        HashSet中  调用hashCode 和  equals
     *        
     *        冒泡排序:把一个数组里面的内容 按照从小到大 或者从大到小

     */
     
     public class BubbleDemo {
        public static void main(String[] args) {
            
            int[] nums = {7,65,10,532,47,45,234,84,3};
            int len = nums.length;
            
            //外层控制趟数
            for (int i = 0; i < len-1; i++) {
                //里控制次数
                for(int j = 0;j < len-1-i;j++){
                    //nums[j] 和 nums[j+1]
                    if(nums[j] > nums[j+1]){
                        int temp = nums[j];
                        nums[j] = nums[j+1];
                        nums[j+1] = temp;
                    }
                }
            }
            
            for (int i = 0; i < nums.length; i++) {
                System.out.println(nums[i]);
            }
            
        }
    }

    冒泡排序优化版:

    package com.baidu_01;

    import java.util.Arrays;

        public class Test3 {
            public static void main(String[] args) {
                //int[] arr = {1,8,5,3,7,6};
                int[] arr = {1,2,3,4,5,6};
                boolean b = true ;
                for(int i = 0; i < arr.length-1 ; i++) {
                    //b = true;
                    for(int j = 0; j < arr.length - 1 -i;j++) {
                        if(arr[j] > arr[j+1]) {
                            int temp = arr[j];
                            arr[j] = arr[j+1];
                            arr[j+1] = temp;
                            b = false;
                        }
                    }            
                    if(b) {
                        System.out.println("遍历了一次");
                        break;
                    }
                }
                System.out.println(Arrays.toString(arr));
            }
        }

    3.TreeSet类

    TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序 和定制排序,其中自然排序为默认的排序方式。向TreeSet中加入的应该是同一个类的对象。

    TreeSet判断两个对象不相等的方式是两个对象通过equals方法返回false,或者通过CompareTo方法比较没有返回0。向TreeSet中添加的应该是同一个类的对象,且最好是不可变对象。

    1.自然排序

    自然排序使用要排序元素的CompareTo(Object obj)方法来比较元素之间大小关系,然后将元素按照升序排列。

    Java提供了一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现了该接口的对象就可以比较大小。

    obj1.compareTo(obj2)方法如果返回0,则说明被比较的两个对象相等,如果返回一个正数,则表明obj1大于obj2,如果是 负数,则表明obj1小于obj2。

    如果我们将两个对象的equals方法总是返回true,则这两个对象的compareTo方法返回应该返回0

    2.定制排序

    自然排序是根据集合元素的大小,以升序排列,如果要定制排序,应该使用Comparator接口,实现 int compare(T o1,T o2)方法,该方法用于比较o1和o2的大小:如果该方法返回正整数,则表示o1大于o2
    ;如果方法返回0,则表示o1等于o2,如果该方法返回负整数,则表示o1小于o2。
     
    Java静态代码块、构造代码块、构造方法的执行顺序 的执行顺序

    静态代码优先于非静态的代码,是因为被static修饰的成员都是类成员,会随着JVM加载类的时候加载而执行,而没有被static修饰的成员也被称为实例成员,需要创建对象才会随之加载到堆内存。所以静态的会优先非静态的。 
    执行构造器(构造方法)的时候,在执行方法体之前存在隐式三步: 
    1,super语句,可能出现以下三种情况: 
    1)构造方法体的第一行是this语句,则不会执行隐式三步, 
    2)构造方法体的第一行是super语句,则调用相应的父类的构造方法, 
    3)构造方法体的第一行既不是this语句也不是super语句,则隐式调用super(),即其父类的默认构造方法,这也是为什么一个父类通常要提供默认构造方法的原因; 
    2,初始化非静态变量; 
    3,构造代码块。 
    由此可知,构造代码块优先于构造方法的方法体,但是this关键字跟super关键字不能同时出现,而且只能在代码的第一行。如果出现了this关键字,隐式三步就不会执行。 
    例如,分析下面的代码及执行结果,已经用注释标出了执行步骤Step 1–Step 7。 
    也就是说,当递归调用多个构造方法的时候,构造代码块只会在最后的(也即方法体第一行不是this语句的)那个构造方法执行之前执行!

    public class Test {
        public static int a = 0;

        static {// Step 1
            a = 10;
            System.out.println("静态代码块在执行a=" + a);
        }

        {// Step 4
            a = 8;
            System.out.println("非静态代码块(构造代码块)在执行a=" + a);
        }

        public Test() {
            this("调用带参构造方法1,a=" + a); // Step 2
            System.out.println("无参构造方法在执行a=" + a);// Step 7
        }

        public Test(String n) {
            this(n, "调用带参构造方法2,a=" + a); // Step 3
            System.out.println("带参构造方法1在执行a=" + a); // Step 6
        }

        public Test(String s1, String s2) {
            System.out.println(s1 + ";" + s2);// Step 5
        }

        public static void main(String[] args) {
            Test t = null;// JVM加载Test类,静态代码块执行
            System.out.println("下面new一个Test实例:");
            t = new Test();
        }
        
        
        public boolean contains(Object c) : 判断是否包含元素
        
        public boolean isEmpty() : 判断是否为空
        
        public int size() : 获取集合长度
        
        Collection中主要方法:
        
        boolean add(E e) : 添加元素
        boolean remove(Object o) : 删除元素
        void clear() : 清空集合
        boolean contains(Object o1) : 判断是否包含某元素
        boolean isEmpty () : 判断是否为空
        int size() : 获取集合长度
        
    }
    1
    33
    执行结果:

    静态代码块在执行a=10
    下面new一个Test实例:
    非静态代码块(构造代码块)在执行a=8
    调用带参构造方法1,a=10;调用带参构造方法2,a=10
    带参构造方法1在执行a=8
    无参构造方法在执行a=8
     
    */ 

    /*

    Arrays : 查API
        static void sort(Object[] obj) : 对传进来的基本类型数组进行排序
        
        static void toString(Objec[] a) : 对传入的数组内容以字符串的形式表现出来.
        
    方法重写:字符类出现了一摸一样的方法(注意:返回值类型可以是子父类)

    Override和Overload的区别?Overload能改变返回值类型吗?
    overload可以改变返回值类型,只看参数列表.

    方法重载:本类中出现的方法名一样,参数列表不同的方法,与返回值类型无关.

    子类对象调用方法的时候:
        先找子类本身,再找父类.
        
    final关键字修饰局部变量:

    1.基本类型,是指不能被改变.
    2.引用类型,是地址值不能被改变,对象中的属性可以改变

    final修饰变量的初始化时:
    1.显示初始化;
    2.在对象构造完毕前即可

    面试:

    要求使用已知的变量,在控制台输出30,20,10

        class Outer {
            public int num = 10;
            class Inner {
                public int num = 20;
                public void show () {
                    int num = 30;
                    syso();
                    syso();
                    syso();
                }
            }
        }
        class InnerClassTest {
            public static void main(String[] args ) {
                Outer.Inner oi = new Outer().new Inner();
                oi.show();
            }
        }

    package , import , class 顺序为:package - import - class 按照这个顺序执行

    代码块的概述:在java中,使用{}括起来的代码被称为代码块.

    A.代码块分为:局部代码块,构造代码块,静态代码块,同步代码块

    代码块应用:

    a.局部代码块:在方法中出现;限定变量声明周期,及早释放,提高内存利用率.

    b.构造代码块(初始化块) : 在类中方法外出现;多个构造方法中相同的代码块放到一起,每次调用构造都指向,并且在构造方法前指向

    c.静态代码块:在类中方法外出现,并加上static 修饰;用于给类进行初始化,在加载的时候就指向,并且只执行一次.
       一般用于加载驱动.
       
     多态调用方法这么执行:
     
     成员变量:编译看左边(父类),运行看左边(父类);
     
     成员方法:编译看左边(父类),运行看右边(子类).动态绑定
     
     静态方法:编译看左边(父类),运行看左边(父类);
     (静态和类相关,算不上重写,所以,访问换是左边的)
     只有非静态方法的成员方法,编译看左边,运行看右边.
     
    内部类访问特点:
    外部类访问内部类必须创建对象.
    外部类名.内部类名 对象名 = 外部类对象.内部类对象

    静态成员内部类:

    static :成员内部类被静态修饰后的访问方式是:
    外部类名.内部类名 对象名 = 外部类.内部类对象;

    面试:
        class A {
            public void show () {
                show2();
            }
            public void show2() {
                syso("我");
            }
        }
        
        class B extends A{
            show(){
                show2();
            }
            show 2 (){
                syso("爱")
            }
        }
        class c extends B{
            show(){
                show2();
            }
            show 2 (){
                syso("你")
            }
        }
        
        public class Test {
            main {
                A a = new B();
                a.show();
                
                B b = new C();
                b.show();
            }
        }

    面试题:
    一个抽象类如果没有抽象方法,可不可以订阅为抽象类,?有什么意思?
    可以,这么做只有一个目的,就是不让其他类创建本类对象,交给子类完成.

        class Test {
            public static void main(String[] stgs ) {
                //Outer.method().show();
                Inter i = Outer.method();
                i.show();
            }
        }
        
        //按照要求补齐代码
        interface Inter {
            void show();
        }
         
         class Outer {
             public static Inter method() {
                 return new Inter() {
                     public void show() {
                         syso("show");
                     }
                 }
             }
         }
     

    方法重写注意事项:
    1.父类中私有方法不能被重写.(父类私有方法子类根本就无法继承)
    2.子类重写父类方法时,访问权限不能更低.
        最后就一致
    3.父类静态方法,子类也必须通过静态方法进行重写.
        其实这个算不算方法重写,但是现象确实如此,至于为什么算不上方法重写
    子类重写父类方法,最好声明一摸一样.

    例题:
    class Fu {
            public Person show() {
                
            }
        }
        class Zi extends Fu {
            public Student show() {
                
            }
        }
        
        class Person {
            
        }
        class Student extends Person {
            
        }

    运算符:instanceof : 用法:
    是双目运算符,左面的操作是一个对象实例,右面是一个类.当左面的对象是右面的类创建的对象时,该运算符
    运算的结果是true,否则是false;

    说明:
    1.一个类的实例包括本身的实例,以及所有直接或间接子类的实例
    2.instanceof左边操作元显示的类型与右边操作元必须是同种类或有继承关系
        即位于继承树的同一个分支上,否则会编译出错.
        

    冒泡排序:让数组中的元素,从小到大存储. 

    相邻比较思想.                                            我们需要比较多少趟
                                        
    第一趟:        我们要比较几次 len-1;                    冒泡排序 : 如果一个数组的长度还是len
                                                        那么要比较   len-1趟
    下标  0-1                                            
                                                        //控制躺数
    下标  1-2                                            for(int i = 0 ; i < len-1; i ++) {
    下标  2-3                                                for(int j = 0 ; j < len-1; j++) {
    下标  3-4                                                    比较num3[j]和num3[j+1]
    下标  4-5
    下标  5-6                                                        
                                                                
                                                            }
                                                        }


    第二趟:
                        len-1-1;
          0-1
          1-2
          2-3
          3-4
          4-5

  • 相关阅读:
    Oracle数据库不能使用索引的原因定位
    C语言中的strncmp
    C标准库函数
    C语言字符串函数大全
    Postgres性能检测
    test cert by python
    如何在C语言中巧用正则表达式
    北大软微一年ABCD
    C 语言字符数组的定义与初始化
    25个设计设计灵感的紫色网站案例
  • 原文地址:https://www.cnblogs.com/haizai/p/11108863.html
Copyright © 2011-2022 走看看