[Java学习日记]泛型、树、Set系列集合

目录

一.泛型

1.泛型类

2.泛型方法

3.泛型接口

4.泛型的继承性与泛型通配符

二.树

二叉平衡树

红黑树(B树)

三.Set系列集合

1.HashSet

2.与运算的特殊情况

3.LinkedHashSet与TreeSet

4.TreeSet加入元素的两种方式


一.泛型

泛型:可以在编译阶段约束操作的数据类型并且进行检查,只能写引用数据类型。
编译的时候会检查泛型,但是转换成class字节码就不会有泛型。
泛型可以写在类,方法,接口后面
1.泛型类
1.如何定义泛型类?
2.在什么情况下使用泛型类?
3.泛型中一般传入什么字符?
案例:定义一个简单的List集合,实现add方法与get方法
public class Demo231 {
    public static void main(String[] args) {
        System.out.println("1.在定义类的时候,如果类里面有成员不确定的时候,就能在类后面接上泛型");
        System.out.println("2.如果类里面的方法中要传递的变量数据类型,要返回的变量数据类型不确定就可以定义带有泛型的类");
        System.out.println("3.public class ArrayList<E>{},一般写Type,Element,Key,Value。");
        MyArrayList<String> arrayList = new MyArrayList<>();
        arrayList.add("aaa");
        System.out.println(arrayList.get(0));
    }
}

class MyArrayList<E> {
    Object[] obj = new Object[10];
    int size;
    public boolean add(E e) {
        obj[size] = e;
        size++;
        return true;
    }
    public E get(int index) {
        return (E) obj[index];
    }
    @Override
    public String toString() {
        return Arrays.toString(obj);
    }
}


2.泛型方法
一样可以在返回类型上与返回传入参数上做限制
如果传入两个参数,这两个参数的类型也可以限制成一样的
泛型声明<>位置在返回类型前
案例:定义工具类,实现方法使得:
1.两个List集合添加到一起
2.list集合添加不确定数量的元素
public class Demo232 {
    public static void main(String[] args) {
        ArrayList<Integer> list1 = new ArrayList<>();
        ArrayList<Integer> list2 = new ArrayList<>();
        list1.add(1);
        list1.add(2);
        list2.add(3);
        list2.add(4);
        System.out.println(ListUtils.addAll2(ListUtils.addAll1(list1,list2),5,6,7,8,9,10));;
    }
}

class ListUtils{
    private ListUtils(){}
    //定义静态方法addAll,用来添加两个集合的元素
    public static <E> ArrayList<E> addAll1(ArrayList<E> list1, ArrayList<E> list2){
        System.out.println("1.泛型声明<>位置在返回类型前");
        System.out.println("2.如果泛型方法中传入两个参数,这两个参数的类型都可以被泛型限制");
        Iterator<E> iterator = list2.iterator();
        while (iterator.hasNext()){
            list1.add(iterator.next());
        }
        return list1;
    }
    public static <E> ArrayList<E> addAll2(ArrayList<E> list1, E...e){
        System.out.println("3.传递:元素类型...变量名 即可传入不定量的元素底层就是数组");
        for (E element:e){
            list1.add(element);
        }
        return list1;
    }
}


3.泛型接口
1.如何定义泛型接口?
2.接口的实现类必须要指定泛型吗?
案例:定义泛型接口与两个不同的实现类
public class Demo233 {
    public static void main(String[] args) {
        System.out.println("1.在接口后限定<>即可定义泛型接口");
        System.out.println("2.实现类能够给出具体类型,或者是继续延续泛型");
        System.out.println(new MyImplement1<String>().method("aaa"));
        System.out.println(new MyImplement2<Integer>().method(666));
    }
}
//这个类的方法返回与传参只能使用String类型
class MyImplement1<String> implements Inter<String>{
    @Override
    public String method(String str){
        return str;
    }
}
//这个类的方法返回与传参能使用任意引用数据类型
class MyImplement2<E> implements Inter<E>{
    @Override
    public E method(E e) {
        return e;
    }
}
interface Inter<E>{
    E method(E e);
}


4.泛型的继承性与泛型通配符
1.方法里面的变量用什么泛型声明的,那么传递进去的变量能用别的泛型声明吗?
2.用这个泛型的对象可以使用其泛型的子类的对象传递数据吗?
3.泛型通配符?有什么作用?
4.具体该如何使用泛型通配符?
public class Demo234 {
    public static void main(String[] args) {
        ArrayList<Father> list1 = new ArrayList<>();
        ArrayList<Son> list2 = new ArrayList<>();
        System.out.println("1.方法里面的变量用什么泛型声明的,那么传递进去的变量也必须使用这个泛型声明");
        //不能传递这种参数:method(list2);
        method(list1);
        method3(list2);
    }
    //在这个地方void前加泛型<Father>声明,会让arrayList的add对象的时候需要把对象变成Father类型(Java对泛型检查保护)
    public static void method(ArrayList<Father> arrayList) {
        System.out.println("2.用这个泛型的对象可以使用其泛型的子类的对象传递数据");
        Son son = new Son();
        arrayList.add(son);
    }
    public static <E> void method2(ArrayList<E> arrayList) {
        Son son = new Son();
        arrayList.add((E) son);
    }
    //在此处无法让集合加入元素,过不了编译
    public static void method3(ArrayList<? extends Father> arrayList) {
        System.out.println("3.使用泛型通配符来限定泛型类型在某一个范围,可以限制某个继承体系的传递的参数");
        System.out.println("4.<?extends E> 表示可以传递E或者E的所有子类");
        System.out.println("<?super E>表示可以传递E或者E的所有父类");
    }
}
class Father {}
class Son extends Father {}


二.树

1.度:每个子节点的数量

2.二叉树:每个节点的度<=2

3.其他概念:树高,根节点,左子节点,右子节点,左子树,右子树

4.二叉查找||排序||搜索树:左子节点比自己小,右子节点比自己大

5.二叉查找树的存储:小的存左边,大的存右边,一样的不存!(最后一点牢记!)

二叉查找树的查找也是同样的流程

6.二叉树的遍历:记忆的话就看当前节点的位置

前序遍历:当前节点-左子节点-右子节点

中序遍历:左子节点-当前节点-右子节点,在遍历二叉查找树,是按照从小到大的顺序去查找的

后序遍历:左子节点-右子节-当前节点,

层序遍历:这个就不解释了

7.二叉查找树的弊端:

如果数据有序,存储只存储在右节点,那么存储的数据就会一边倒,存储效率很快就会变低

要是存储的时候能够旋转一下平衡平衡就好了:如此引入平衡二叉树


二叉平衡树

8.平衡二叉树:在二叉查找树的基础上新增规则:任意节点的左右子树高度叉不能超过一

9.通过左右旋转保证平衡(添加一个节点之后,该树不再是二叉树)

确定支点是从添加的节点开始不断找不平衡的父节点

10.假设不平衡的节点为A

(1)左旋:把A降级为其左子节点,把A原本的右子节点晋升,取代A

(2)如果这个时候晋升的节点有左子树,丢掉。把它给A,变成A的右子树(因为原本A的右子树所有节点都比它大)

(3)右旋:把A降级为其右子节点,把A原本的右子节点晋升,取代A

(4)如果这个时候晋升的节点有右子树,把它给A,变成A的左子树

11.需要旋转的四种情况:

(1)左左:根节点左子树的左子树有节点插入,一次右旋就能解决

(2)左右:根节点左子树的右子树右节点插入,要先左旋再右旋:

如果和左左一样,那么你就会发现A得到的新的左子树还是在原本的层级,树的长度并没有减少,直接右旋是无效的

所以要先在根节点的左子节点进行左旋,把情况变得和左左一样,再到根节点右旋

(3)右左:和左右一样,直接左旋不行,需要先在根节点的右子节点进行右旋,再到根节点进行左旋

(4)右右:直接左旋。


红黑树(B树)

红黑树:自平衡的二叉查找树,也叫二叉B树

红黑树每个节点都有存储位表示节点的颜色,每一个节点可以是黑或者红,不是高度平衡,而是红黑规则实现!!!!

平衡二叉树虽然查找的时间快,但是添加的时候就有点麻烦

红黑树是一颗二叉查找树,不是高度平衡,满足红黑规则(没有二叉平衡树那么严格)

红黑规则:

1.节点必须是红色或者是黑色

2.根节点为黑色

3.如果一个节点缺失了左节点或者右节点,这个节点会记录空的黑色节点Nil代表叶子节点

4.红色节点的子节点必须是黑色

5.每个节点到叶节点的所有路径中:经过的黑色节点是相等的

对于每一个节点:多出颜色属性记录颜色

添加红黑树默认添加红色会更快,添加过程:

1.根节点是黑色

2.如果父亲是黑色,加入红色节点不会右任何影响

3.如果父亲是红色,就要看叔叔

(1)如果叔叔和父亲全是红色,那么就把他们两个全部变为黑色·,祖父变成红色

如果祖父为根,就变回黑,不是根就把祖父设置成为当前的节点在进行判断

(2)如果叔叔是黑色,自己是左孩子,就需要把父亲变为黑色(和叔叔一样),再把祖父设置为红色,再把祖父右旋

(3)如果叔叔是黑色,自己是右孩子,先左旋父亲,变成(2)情况,再进行判断


三.Set系列集合

对于Set系列:HashSet,LinkedHashSet,TreeSet,底层是用Map创建
Set系列中的方法和collection类似,可以使用迭代器,增强for,foreach方法遍历
HashSet:无序,不重复无索引
LinkedHashSet:有序,不重复无索引
TreeSet:可排序,不重复无索引
1.HashSet
Hash值:对象的整数表现形式
1.用什么方法获取Hash值?
2.获取自定义对象的时候默认获取到Hash值是什么?
3.什么是Hash碰撞?
4.一般情况下如何获取自定义对象的Hash值?
JDK8以前的底层:在JDK8之后采用数组+链表+红黑树
1.创建一个长度为16,默认加载因子为0.75(hashSet扩容时机)的数组,数组名为table
2.加入元素时:根据hash值与数组长度计算index:index=(arr.length()-1)&hash值,这里的值由于&,会使得计算的结果一定会小于数组长度
3.如果加入的元素hash值一样,而属性值不一样,那就挂在新元素下面(JDK8以前是头插法,与JDK8以后相反)
4.当链表长度大于8,数组长度大于64,就会把链表自动转成红黑树

public class Demo235 {
    public static void main(String[] args) {
        Student student1 = new Student("张三", 23);
        Student student2 = new Student("张三", 23);
        Student student3 = new Student("李四", 24);
        Student student4 = new Student("王五", 25);

        System.out.println("1根据hashCode方法计算出来的整数,定义在Object类中,所有对象都可以调用");
        System.out.println("2.获取Hash值,默认使用地址值计算,但是一般情况下会重写HashCode方法,利用它对象内部属性计算哈希值");
        int hash1 = student1.hashCode();
        int hash2 = student2.hashCode();
        System.out.println(hash1+" "+hash2);

        System.out.println("3.在小部分情况下,会出现Hash碰撞:不同对象不同属性的对象计算出来的hash值一样");
        System.out.println("abd".hashCode()+" "+"acD".hashCode());

        System.out.println("4.当hashSet中存储自定义对象的时候请重写hashCode(得到哈希值以索引位置)与equals(比较对象属性,相同则不加入表)方法");
        HashSet<Student> hashSet = new HashSet<>();
        hashSet.add(student4);
        hashSet.add(student3);
        hashSet.add(student2);
        hashSet.add(student1);
        System.out.println(hashSet);
    }
}

public class Student implements Comparable<Student> {
    private String name;
    private int age;

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Objects.equals(name, student.name);
    }

    @Override
    public int compareTo(Student o) {
        return this.getAge() - o.getAge();
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Student() {
    }

    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 "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}


2.与运算的特殊情况
//与运算中的特殊情况:某一位数的后段全是1
public class Demo236 {
    public static void main(String[] args) {
        System.out.println("对于求index = hash*(arr.length()-1):当arr的长度是2的整数倍的时候");
        System.out.println("arr.length()-1的补码就全是1,这个是时候求a&b就相当于大数除以小数留下来的余数");
        //00000000 00000000 00000000 00001111
        //00000000 00000000 00000000 01010000
        int a = 15;
        int b = 67;
        System.out.println((a&b));
    }
}
 


3.LinkedHashSet与TreeSet
1.LinkedHashSet与HashSet有什么关系?
2.相比于HashSet有声明特点?
3.底层如何实现?
4.Tree与HashSet有什么关系?
5.相比于HashSet有声明特点?
6.底层用什么实现?
public class Demo237 {
    public static void main(String[] args) {
        System.out.println("1.LinkedHashSet是HashSet的子类");
        System.out.println("2.相比于HashSet,其存取是有序的");
        System.out.println("3.底层的节点能够形成双向链表");
        Student student1 = new Student("张三", 23);
        Student student2 = new Student("张三", 23);
        Student student3 = new Student("李四", 24);
        Student student4 = new Student("王五", 25);
        LinkedHashSet<Student> linkedHashSet = new LinkedHashSet<>();
        linkedHashSet.add(student4);
        linkedHashSet.add(student3);
        linkedHashSet.add(student2);
        linkedHashSet.add(student1);
        System.out.println(linkedHashSet);
        System.out.println("4.Tree与HashSet同级");
        System.out.println("5.相比于HashSet,它是可排序的,默认按照从小到大排序");
        System.out.println("6.TreeSet底层基于红黑树实现,增删改查性能良好(注意这里就没有Hash表了!!)");
        TreeSet<Integer> treeSet = new TreeSet<>();
        treeSet.add(5);
        treeSet.add(6);
        treeSet.add(4);
        treeSet.add(1);
        System.out.println(treeSet);
    }
}


4.TreeSet加入元素的两种方式
public class Demo238 {
    public static void main(String[] args) {
        System.out.println("方法一:在自定义类中实现comparable接口");
        Student student1 = new Student("李四",24);
        Student student2 = new Student("王五",26);
        Student student3 = new Student("赵六",25);
        Student student4 = new Student("张三",23);
        TreeSet<Student> treeSet = new TreeSet<>();
        treeSet.add(student1);
        treeSet.add(student2);
        treeSet.add(student3);
        treeSet.add(student4);
        System.out.println(treeSet);

        System.out.println("方法二:创建TreeSet对象的时候,传递Comparator指定规则,这个方法优先级更高");
        TreeSet<String> treeSet1 = new TreeSet<>();
        TreeSet<String> treeSet2 = new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                int result = o1.length() - o2.length();
                if (result == 0) result = o1.compareTo(o2);
                return result;
            }
        });
        treeSet1.add("c");
        treeSet1.add("ab");
        treeSet1.add("bcd");
        treeSet1.add("qwqwq");
        treeSet2.add("c");
        treeSet2.add("ab");
        treeSet2.add("bcd");
        treeSet2.add("qwqwq");
        System.out.println(treeSet1);
        System.out.println(treeSet2);
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值