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

点赞