集合类(collection、List、Set、Map、HashMap、TreeMap、、Properties、泛型类、可变长参数、增强for循环、Collections、数组与链表)

0.集合类概述

在这里插入图片描述

1. 为什么需要集合类?
很多情况下,我们需要对一组对象进行操作。为了解决事先并不知道到底有多少个对象,Java 提供了集合类供我们使用。(存储更多类型问题, 扩容问题, 内存空间浪费问题, 数据查找问题, 数据删除问题等等)
2. 集合类的特点
a. 只能存储引用数据类型
b. 可以自动地调整自己的大小
3. 数组和集合类都是容器,它们有何不同?
a. 数组可以存储基本数据类型的数据,集合不可以。
b. 数组的长度是固定的,集合可以自动调整自己的大小。
c. 数组效率高,集合效率较低。
d. 数组没有API,集合有丰富的API

数据结构:
数据结构是相互之间存在一种或多种特定关系的数据元素的集合。
元素之间,通常有以下四种基本结构:
1.集合
2.线性结构
3.树形结构
4.图或网状结构

逻辑结构&物理结构
逻辑结构:数据元素间的逻辑关系
物理结构:数据结构在计算机中的表示,又称为存储结构或者映像。

结构的表示可以分为两种:顺序存储结构 (顺序映像) 和 链式存储结构 (非顺序映像)。

顺序映像:借助元素在存储器中的相对位置来表示数据元素之间的逻辑关系。(数组)

非顺序映像:借助指示元素存储地址的”指针”,来表示数据元素的逻辑关系。(链表)

1.Collection

概念

在这里插入图片描述

Collection 层次结构中的根接口Collection 表示一组对象,这些对象也称为 collection 的元素

  • 一些 collection 允许有重复的元素,而另一些则不允许
  • 一些 collection 是有序的,而另一些则是无序的
public class CollectionDemo4 {
    public static void main(String[] args) {
        ArrayList collection1 = new ArrayList();//ArrayList就是动态数组
        collection1.add("zs");
        collection1.add("zs");

        HashSet collection2 = new HashSet();//称为集合,该容器中只能存储不重复的对象。
        // 它是基于 HashMap 实现的,底层采用 HashMap 来保存元素
        collection2.add("zs");
        collection2.add("zs");

        System.out.println(collection1);//输出[zs, zs]
        System.out.println(collection2);//输出[zs]
    }
}

在这里插入图片描述

(1)Collection接口的API

在这里插入图片描述
1.boolean add(E e):确保此 collection 包含指定的元素(可选操作)
2.boolean remove(Object o):从此 collection 中移除指定元素的单个实例,如果存在的话(可选操作)。
3.void clear():移除此 collection 中的所有元素(可选操作)。
4.boolean contains(Object o):如果此 collection 包含指定的元素,则返回 true
5.boolean isEmpty():如果此 collection 不包含元素,则返回 true
6.int size():返回此 collection 中的元素数
7.boolean addAll(Collection c)
8.boolean removeAll(Collection c):移除此 collection 中那些也包含在指定 collection 中的所有元素(可选操作)。
9.boolean containsAll(Collection c):如果此 collection 包含指定 collection 中的所有元素,则返回 true
10.boolean retainAll(Collection c):仅保留此 collection 中那些也包含在指定 collection 的元素(可选操作)
11.boolean equals(Object o):比较此 collection 与指定对象是否相等
12. int hashCode() :返回此 collection 的哈希码值

public class CollectionDemo6 {
    public static void main(String[] args) {
        //添加元素
        Collection collection = new ArrayList();
        collection.add("ff");
        collection.add("ggf");
        collection.add("sf");
        collection.add("gf");
        collection.add("eh");
        System.out.println("添加元素:"+collection);

//添加所有元素
        Collection collection2 = new ArrayList();
        collection2.addAll(collection);
        System.out.println("添加所有元素:"+collection2);

//查找是否存在
        boolean ff = collection.contains("ff");
        boolean ff2 = collection.contains("ffeee");
        System.out.println(ff);
        System.out.println(ff2);
        boolean ff3 = collection.containsAll(collection2);
        System.out.println(ff3);


        boolean equals = collection.equals(collection2);
        System.out.println("查找集合是否存在:"+equals);

//删除一个元素
        Collection collection3 = new ArrayList();
        collection3.add("sf");
        collection3.add("yt");
        collection3.add("yd");
        System.out.println("输出当前集合:"+collection3);
        boolean sf = collection3.remove("sf");
        System.out.println("是否删除成功:"+sf);
        System.out.println("删除一个元素sf后的集合:"+collection3);

        Collection collection4 = new ArrayList();
        collection4.add("yt");
        collection4.add("yd");
        collection3.removeAll(collection4);
        System.out.println("删除所有元素后collection3的集合:"+collection3);
        System.out.println("collection3的集合:"+collection4);

        //保存元素
        Collection collection5 = new ArrayList();
        collection5.add("eh");
        collection5.add("zzz");
        collection.retainAll(collection5);
        System.out.println("保存元素:"+collection);

        //获得所有存储元素的个数
        int size = collection.size();
        System.out.println("获得所有存储元素的个数:"+size);
        //判断是否为空
        boolean empty = collection.isEmpty();
        System.out.println("判断是否为空:"+empty);
        //清空容器中所有元素
        collection.clear();
        System.out.println("清空容器中所有元素:"+collection);
    }
}

结果输出:

添加元素:[ff, ggf, sf, gf, eh]
添加所有元素:[ff, ggf, sf, gf, eh]
true
false
true
查找集合是否存在:true
输出当前集合:[sf, yt, yd]
是否删除成功:true
删除一个元素sf后的集合:[yt, yd]
删除所有元素后collection3的集合:[]
collection3的集合:[yt, yd]
保存元素:[eh]
获得所有存储元素的个数:1
判断是否为空:false
清空容器中所有元素:[]

13.Iterator iterator():迭代器,集合的专用遍历方式
14.Object[] toArray(): 返回包含此 collection 中所有元素的数组
[1] 这个数组足够长:会返回一个和给定长度相等的数组, 剩余空间用null填充;且当做参数的数组, 和返回的数组是等价的。
[2]若这个数组不够长:返回的数组等价于集合类中存储的元素数目;且当做参数的数组, 全是null。

public class CollectionDemo7 {
    public static void main(String[] args) {
        Collection collection1 = new ArrayList();
         collection1.add("zs");
        collection1.add("ls");

        // Object[] toArray()
        //          返回包含此 collection 中所有元素的数组。
        //<T> T[]
        // toArray(T[] a)
        //          返回包含此 collection 中所有元素的数组;返回数组的运行时类型与指定数组的运行时类型相同。

        Object[] objs = new Object[10];//创建单位为10的数组
        Object[] objects = collection1.toArray(objs);//将collection1集合中的元素放在单位为10的数组中
        for (int i = 0; i <objects.length ; i++) {
            System.out.println("数组足够长:"+objects[i]);
        }

        Object[] objs2 = new Object[1];//创建单位为10的数组
        Object[] objects2 = collection1.toArray(objs2);//将collection1集合中的元素放在单位为10的数组中
        for (int i = 0; i <objects2.length ; i++) {
            System.out.println("数组不足够长:"+objects[i]);
        }
        
        Object[] objs3 = new Object[1];//创建单位为10的数组
        Object[] objects3 = collection1.toArray(objs3);//将collection1集合中的元素放在单位为10的数组中
        for (int i = 0; i <objs3.length ; i++) {
            System.out.println("数组不足够长(参数数组):"+objs3[i]);
        }
    }
}

结果输出:

数组足够长:null
数组足够长:null
数组足够长:null
数组足够长:null
数组足够长:null
数组足够长:null
数组足够长:null
数组足够长:null
数组不足够长:zs
数组不足够长:ls
数组不足够长(参数数组)null

(2)Iterator接口(collection集合的遍历)

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 它是对集合进行迭代的迭代器
  • 依赖于集合对象存在
  • boolean hasNext():如果仍有元素可以迭代,则返回true
  • E next():返回迭代的下一个元素
  • void remove(): 删除上一次遍历过的元素
public class CollectionDemo8 {
    public static void main(String[] args) {
        Collection collection1 = new ArrayList();
        collection1.add("zs");
        collection1.add("ls");
        collection1.add("ls");
        collection1.add("ls");
        collection1.add("ls");
        //Tterator iterator() 返回了一个迭代器
        //迭代:循环遍历
        Iterator iterator = collection1.iterator();
        boolean b = iterator.hasNext();//如果仍有元素可以迭代,则
        System.out.println("是否仍有元素可以迭代:"+b);

        Object next = iterator.next();//     返回迭代的下一个元素
        System.out.println("返回迭代的下一个元素:"+next);

        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

    }
}

CollectionDemo8结果输出;

是否仍有元素可以迭代:true
返回迭代的下一个元素:zs
ls
ls
ls
ls
public class CollectionDemo9 {
    public static void main(String[] args) {
        Collection collection1 = new ArrayList();
        collection1.add("zs");
        collection1.add("ls");
        collection1.add("lh");
        collection1.add("aeth");
        collection1.add("hsd");
        System.out.println(collection1);
        Iterator it = collection1.iterator();
        //remove()
        //     删除上一次遍历(迭代)过的元素,不能连续删除,不能在遍历之前删除
        //应用:在遍历过程中,某个元素不符合预期,把这个元素删除
        Object next = it.next();
        System.out.println("当前迭代的元素:"+next);
        it.remove();
        System.out.println("删除上一次迭代的元素后:"+collection1);
        Object next2 = it.next();
        System.out.println("当前迭代的元素:"+next2);
        it.remove();
        System.out.println("删除上一次迭代的元素后:"+collection1);
    }

}

CollectionDemo9结果输出:

[zs, ls, lh, aeth, hsd]
当前迭代的元素:zs
删除上一次迭代的元素后:[ls, lh, aeth, hsd]
当前迭代的元素:ls
删除上一次迭代的元素后:[lh, aeth, hsd]

案例:collection集合存储学生对象并遍历

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

(3)并发修改异常

ConcurrentModificationException
在这里插入图片描述

产生原因
迭代器遍历的过程中,通过集合对象修改了集合中的元素,造成了迭代器获取元素中判断预期修改值实际修改值不一致

在iterator迭代中, 通过iterator返回的迭代对象, 会保存原集合数据的修改次数, 并且这个iterator再每次通过遍历的时候, 会重复检查, 保存修改次数, 和此时此刻的源集合类的修改次数是否还一样, 如果不一样, 意味着, 这个源集合类在迭代的过程中, 被别人修改了, 这个iterator就会抛出并发修改异常。
解决方案;
用for循环遍历,然后用集合对象做对应的操作即可

ps:ListIteratror:列表迭代器
可以实现上述功能,而不产生错误

public class CollectionDemo10 {
    public static void main(String[] args) {
        Collection collection1 = new ArrayList();
        collection1.add("zs");
        collection1.add("ls");
        collection1.add("lh");
        collection1.add("aeth");
        collection1.add("hsd");
        System.out.println(collection1);
        //ConcurrentModificationException并发修改异常
        //在iterator迭代中, 通过iterator返回的迭代对象, 会保存原集合数据的修改次数,
        // 并且这个iterator再每次通过遍历的时候, 会重复检查, 保存修改次数,
        // 和此时此刻的源集合类的修改次数是否还一样, 如果不一样, 意味着, 这个源集合类在迭代的过程中,
        // 被别人修改了, 这个iterator就会抛出并发修改异常
        Iterator it = collection1.iterator();
        it.next();//刚刚遍历完it对象代码的集合collection1,
        collection1.remove("zs");//马上就删除了collection1中的zs
        it.next();//????

        //collection1.remove("zs");//解决方案
    }
}

结果输出;
在这里插入图片描述
改进后;

public class CollectionDemo10 {
    public static void main(String[] args) {
        Collection collection1 = new ArrayList();
        collection1.add("zs");
        collection1.add("ls");
        collection1.add("lh");
        collection1.add("aeth");
        collection1.add("hsd");
        System.out.println(collection1);
        //ConcurrentModificationException并发修改异常
        //在iterator迭代中, 通过iterator返回的迭代对象, 会保存原集合数据的修改次数,
        // 并且这个iterator再每次通过遍历的时候, 会重复检查, 保存修改次数,
        // 和此时此刻的源集合类的修改次数是否还一样, 如果不一样, 意味着, 这个源集合类在迭代的过程中,
        // 被别人修改了, 这个iterator就会抛出并发修改异常
        Iterator it = collection1.iterator();
        it.next();//刚刚遍历完it对象代码的集合collection1,
        //collection1.remove("zs");//马上就删除了collection1中的zs
        it.next();//????
        collection1.remove("zs");//解决方案
    }
}

结果输出;
在这里插入图片描述

怎么避免抛出并发修改异常:
1.多线程, 没有办法避免,
2.单线程, 迭代的过程还没有迭代结束, 就使用了源集合类的方法修改了源集合类.

注意事项:

  1. 用迭代器对集合遍历的时候,不要使用集合的API对集合进行修改
  2. 使用集合对象的时候,不要使用while循环,可以使用for循环,最好使用foreach循环。

(4)Foreach循环
Foreach/加强的for循环/ 增强的for循环
for (Object str : collection1) {
System.out.println(str);
}

foreach循环, 在编译之后, 会转化为iterator迭代
注意:
1, 只有实现iterator方法的集合类,才可以使用foreach循环 (数组)
2, 不要在使用foreach的时候, 调用源集合类的方法修改源集合类(会产生并发修改异常) -> 如果使用foreach循环, 一般仅仅是用来查找元素
循环示例1:

public class CollectionDemo11 {
    public static void main(String[] args) {
        Collection collection1 = new ArrayList();
        collection1.add("zs");
        collection1.add("ls");
        collection1.add("lh");
        collection1.add("aeth");
        collection1.add("hsd");
        System.out.println(collection1);

        Object[] objects = collection1.toArray();
        for (int i = 0; i <objects.length ; i++) {
            System.out.println("遍历方式一:"+objects[i]);
        }
        System.out.println("--------");
        Iterator iterator = collection1.iterator();
        while (iterator.hasNext()){
            System.out.println("遍历方式二:"+iterator.next());
        }
    }
}

结果输出;

[zs, ls, lh, aeth, hsd]
遍历方式一:zs
遍历方式一:ls
遍历方式一:lh
遍历方式一:aeth
遍历方式一:hsd
--------
遍历方式二:zs
遍历方式二:ls
遍历方式二:lh
遍历方式二:aeth
遍历方式二:hsd

循环示例2:

public class CollectionDemo12 {
    public static void main(String[] args) {
        Collection collection1 = new ArrayList();
        collection1.add("zs");
        collection1.add("ls");
        collection1.add("lh");
        collection1.add("aeth");
        collection1.add("hsd");
        System.out.println(collection1);

        for (Object str : collection1) {
            System.out.println("加强版for循环:" + str);
        }
        //在class文件中,实际上就是迭代循环
        // Iterator var2 = collection1.iterator();
        //
        //        while(var2.hasNext()) {
        //            Object str = var2.next();
        //            System.out.println("加强版for循环:" + str);
        //        }

        String[] strs = {"zs", "ls", "zf"};

        for (String str : strs) {
            strs[0]=null;//值传递,引用传递?
            System.out.println(str);
        }

    }
}

结果输出;

[zs, ls, lh, aeth, hsd]
加强版for循环:zs
加强版for循环:ls
加强版for循环:lh
加强版for循环:aeth
加强版for循环:hsd
zs
ls
zf

Collection 小结
Collection不同实现子类, 底层机构不一样 -> 每一个实现子类的遍历方式必然不一样
在这里插入图片描述

1.1List及其子类

在这里插入图片描述

线性表:n个数据元素的有序序列。
1.首先,线性表中元素的个数是有限的。
2.其次,线性表中元素是有序的。
List接口概述:
有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。

有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。

set 不同,列表通常允许重复的元素。更确切地讲,列表通常允许满足 e1.equals(e2) 的元素对 e1 和 e2,并且如果列表本身允许 null 元素的话,通常它们允许多个 null 元素
在这里插入图片描述

List接口API

在这里插入图片描述

1.void add(int index,E element): 在列表的指定位置插入指定元素(可选操作)
2.E remove(int index):移除列表中指定位置的元素(可选操作)。
3.E get(int index): E get(int index)
返回列表中指定位置的元素。
4.E set(int index,E element):用指定元素替换列表中指定位置的元素(可选操作)。
5.ListIterator listIterator():返回此列表元素的列表迭代器(按适当顺序)

ListIterator listIterator(int index) :返回列表中元素的列表迭代器(按适当顺序),从列表的指定位置开始。

6.boolean hasPrevious()
7.E previous()
8.boolean addAll(int index, Collection<? extends E> c) :将指定 collection 中的所有元素都插入到列表中的指定位置(可选操作)
9. int indexOf(Object o):返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。
10. int lastIndexOf(Object o) :返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1。
11. List subList(int fromIndex, int toIndex):返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图

public class ListDemo02 {
    public static void main(String[] args) {
        //创建集合对象
        List<String> list = new ArrayList<String>();

        //添加元素
        list.add("hello");
        list.add("world");
        list.add("java");

        //void add(int index,E element):在此集合中的指定位置插入指定的元素
//        list.add(1,"javaee");
        //IndexOutOfBoundsException
//        list.add(11,"javaee");

        //E remove(int index):删除指定索引处的元素,返回被删除的元素
//        System.out.println(list.remove(1));
        //IndexOutOfBoundsException
//        System.out.println(list.remove(11));

        //E set(int index,E element):修改指定索引处的元素,返回被修改的元素
//        System.out.println(list.set(1,"javaee"));
        //IndexOutOfBoundsException
//        System.out.println(list.set(11,"javaee"));

        //E get(int index):返回指定索引处的元素
//        System.out.println(list.get(1));
        //IndexOutOfBoundsException
//        System.out.println(list.get(11));

        //输出集合对象
//        System.out.println(list);

        //遍历集合
//        System.out.println(list.get(0));
//        System.out.println(list.get(1));
//        System.out.println(list.get(2));

        //用for循环改进遍历
        for (int i=0; i<list.size(); i++) {
            String s = list.get(i);
            System.out.println(s);
        }
    }
}

案例:List集合存储学生对象并遍历

在这里插入图片描述
Student

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {


        this.name = name;
        this.age = age;
    }

    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;
    }
}

ListDemo

public class ListDemo {
    public static void main(String[] args) {
        //创建List集合对象
        List<Student> list = new ArrayList<Student>();

        //创建学生对象
        Student s1 = new Student("林青霞", 30);
        Student s2 = new Student("张曼玉", 35);
        Student s3 = new Student("王祖贤", 33);

        //把学生添加到集合
        list.add(s1);
        list.add(s2);
        list.add(s3);

        //迭代器方式
        Iterator<Student> it = list.iterator();
        while (it.hasNext()) {
            Student s = it.next();
            System.out.println(s.getName() + "," + s.getAge());
        }
        System.out.println("--------");

        //for循环方式
        for(int i=0; i<list.size(); i++) {
            Student s = list.get(i);
            System.out.println(s.getName() + "," + s.getAge());
        }

    }
}

在这里插入图片描述

ListIteratror:列表迭代器

在这里插入图片描述

Listiterator 是 iterator的子接口, 功能上是iterator的增强(可沿任意方向遍历列表的迭代器), 它提供了更丰富的api

1.void add(E e) :将指定的元素插入列表(可选操作)
2. boolean hasNext() :以正向遍历列表时,如果列表迭代器有多个元素,则返回 true(换句话说,如果 next 返回一个元素而不是抛出异常,则返回 true)。
3. boolean hasPrevious() :如果以逆向遍历列表,列表迭代器有多个元素,则返回 true。
4. E next() :返回列表中的下一个元素
5. int nextIndex() :返回对 next 的后续调用所返回元素的索引
6. E previous():返回列表中的前一个元素。
7. int previousIndex() : 返回对 previous 的后续调用所返回元素
8. void remove() : 从列表中移除由 next 或 previous 返回的最后一个元素(可选操作)。
9. void set(E e) :用指定元素替换 next 或 previous 返回的最后一个元素(可选操作)。

在这里插入图片描述

public class ListIteratorDemo {
    public static void main(String[] args) {
        //创建集合对象
        List<String> list = new ArrayList<String>();

        //添加元素
        list.add("hello");
        list.add("world");
        list.add("java");

        //通过List集合的listIterator​()方法得到
//        ListIterator<String> lit = list.listIterator();
//        while (lit.hasNext()) {
//            String s = lit.next();
//            System.out.println(s);
//        }
//        System.out.println("--------");
//
//        while (lit.hasPrevious()) {
//            String s = lit.previous();
//            System.out.println(s);
//        }

        //获取列表迭代器
        ListIterator<String> lit = list.listIterator();
        while (lit.hasNext()) {
            String s = lit.next();
            if(s.equals("world")) {//不会产生并发修改异常
                lit.add("javaee");//最后的实际修改值会赋值给预期修改值
            }
        }

        System.out.println(list);

    }
}

在这里插入图片描述

List集合存储学生对象三种方式遍历

在这里插入图片描述
Student

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {


        this.name = name;
        this.age = age;
    }

    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;
    }
}

ListDemo

public class ListDemo {
    public static void main(String[] args) {
        //创建List集合对象
        List<Student> list = new ArrayList<Student>();

        //创建学生对象
        Student s1 = new Student("林青霞", 30);
        Student s2 = new Student("张曼玉", 35);
        Student s3 = new Student("王祖贤", 33);

        //把学生添加到集合
        list.add(s1);
        list.add(s2);
        list.add(s3);

        //迭代器:集合特有的遍历方式
        Iterator<Student> it = list.iterator();
        while (it.hasNext()) {
            Student s = it.next();
            System.out.println(s.getName()+","+s.getAge());
        }
        System.out.println("--------");

        //普通for:带有索引的遍历方式
        for(int i=0; i<list.size(); i++) {
            Student s = list.get(i);
            System.out.println(s.getName()+","+s.getAge());
        }
        System.out.println("--------");

        //增强for:最方便的遍历方式
        for(Student s : list) {
            System.out.println(s.getName()+","+s.getAge());
        }
    }
}

在这里插入图片描述

List的子类(ArrayList、Vector、LinkedList)

  • ArrayList
    底层数据结构是数组,查询快,增删慢
    线程不安全,效率高
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • Vector
    底层数据结构是数组,查询快,增删慢;
    线程安全,效率低;
    Vector 特有的API;
    public void addElement(E obj)
    public E elementAt(int index)
    public Enumeration elements()

  • LinkedList
    底层数据结构是链表,查询慢,增删快
    线程不安全,效率高
    在这里插入图片描述

LinkedList 特有的API
① public void addFirst(E e)及addLast(E e)
② public E getFirst()及getLast()
③ public E removeFirst()及public E removeLast()

在这里插入图片描述

练习:
去重
请用ArrayList实现栈数据结构,并测试。
3. 集合的嵌套遍历
4. 获取10个1-20之间的随机整数,要求集合中的数不能重复。

1.2 Set及其子类

Set 概述

一个不包含重复元素的 collection。更确切地讲, set 不包含满足 e1.equals(e2) 的元素对 e1 和 e2, 并且最多包含一个 null 元素

注意事项:Set 集合并不一定都是无序的,有些 Set 集合是有序的。

(1)HashSet 概述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 底层是HashMap
  • 不保证迭代顺序,更不保证该顺序恒久不变 (无序的)
  • 允许存储 null 元素
  • 不同步

HashSet 的元素具有唯一性(Set 存储的元素是作为 Map 的 key,而Map的key是唯一的)
HashSet 依赖于存储元素的两个方法: int hashCode() & boolean equals(Object obj)
注意事项:千万不要修改 HashSet 元素的属性值!
在这里插入图片描述
在这里插入图片描述

public class CollectionDemo5 {
    public static void main(String[] args) {
        ArrayList collection1 = new ArrayList();//ArrayList就是动态数组
        collection1.add("zs");
        collection1.add("ls");
        collection1.add("wu");
        collection1.add("z1");
        collection1.add("1");

        Collection collection2 = new HashSet();//HashSet添加数据是随机加入
        collection2.add("zs");
        collection2.add("ls");
        collection2.add("wu");
        collection2.add("z1");
        collection2.add("1");
        collection2.add("有");

        System.out.println(collection1);//输出[zs, ls, wu, z1, 1]
        System.out.println(collection2);//输出[1, ls, z1, zs, 有, wu],不按顺序
    }
}

在这里插入图片描述

HashSet集合存储学生对象并遍历

在这里插入图片描述
在这里插入图片描述

(2)LinkedHashSet 概述

  • HashSet 的子类
  • 底层是HashMap & 双向链表
  • HashMap 保证了元素的唯一性
  • 链表定义了迭代的顺序,按照元素的插入顺序进行迭代
  • 不同步
    在这里插入图片描述

(3)TreeSet 概述

在这里插入图片描述
在这里插入图片描述

自然排序Comparable的使用

在这里插入图片描述
Student

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

    public Student() {
    }

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

    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 int compareTo(Student s) {
//        return 0;
//        return 1;
//        return -1;
        //按照年龄从小到大排序
       int num = this.age - s.age;
//        int num = s.age - this.age;
        //年龄相同时,按照姓名的字母顺序排序
       int num2 = num==0?this.name.compareTo(s.name):num;
        return num2;
    }
}

TreeSetDemo02

public class TreeSetDemo02 {
    public static void main(String[] args) {
        //创建集合对象
        TreeSet<Student> ts = new TreeSet<Student>();

        //创建学生对象
        Student s1 = new Student("xishi", 29);
        Student s2 = new Student("wangzhaojun", 28);
        Student s3 = new Student("diaochan", 30);
        Student s4 = new Student("yangyuhuan", 33);

        Student s5 = new Student("linqingxia",33);
        Student s6 = new Student("linqingxia",33);

        //把学生添加到集合
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);

        //遍历集合
        for (Student s : ts) {
            System.out.println(s.getName() + "," + s.getAge());
        }
    }
}

在这里插入图片描述

比较器排序Comparable的使用

在这里插入图片描述

Student

public class Student {
    private String name;
    private int chinese;
    private int math;

    public Student() {
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getChinese() {
        return chinese;
    }

    public void setChinese(int chinese) {
        this.chinese = chinese;
    }

    public int getMath() {
        return math;
    }

    public void setMath(int math) {
        this.math = math;
    }

    public int getSum() {
        return this.chinese + this.math;
    }
}
public class TreeSetDemo {
    public static void main(String[] args) {
        //创建TreeSet集合对象,通过比较器排序进行排序
        TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
//                int num = (s2.getChinese()+s2.getMath())-(s1.getChinese()+s1.getMath());
                //主要条件
                int num = s2.getSum() - s1.getSum();
                //次要条件
                int num2 = num == 0 ? s1.getChinese() - s2.getChinese() : num;
                int num3 = num2 == 0 ? s1.getName().compareTo(s2.getName()) : num2;
                return num3;
            }
        });

        //创建学生对象
        Student s1 = new Student("林青霞", 98, 100);
        Student s2 = new Student("张曼玉", 95, 95);
        Student s3 = new Student("王祖贤", 100, 93);
        Student s4 = new Student("柳岩", 100, 97);
        Student s5 = new Student("风清扬", 98, 98);

        Student s6 = new Student("左冷禅", 97, 99);
//        Student s7 = new Student("左冷禅", 97, 99);
        Student s7 = new Student("赵云", 97, 99);

        //把学生对象添加到集合
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        ts.add(s6);
        ts.add(s7);

        //遍历集合
        for (Student s : ts) {
            System.out.println(s.getName() + "," + s.getChinese() + "," + s.getMath() + "," + s.getSum());
        }
    }
}

在这里插入图片描述

  • TreeSet 底层的数据结构是 TreeMap, TreeMap的底层是红黑树
  • 若创建对象时,传入了 Comparator 对象,根据 Comparator 进行排序,否则根据自然顺排序。
  • 不能存储 null 元素除非Comparator中定义的null的比较规则
  • 不同步

TreeSet 的元素具有唯一性(依赖于存储元素的 compareTo(obj) 方法,或者是传入的 Comparator 对象的 compare(o1, o2) 方法)

注意事项:千万不要修改 TreeSet 元素的属性值!

除Set接口中定义的方法外,由于TreeSet 中的元素是大小有序的,因此它还有一些特殊的方法。
1.E first();
2.E last();
3.E pollFirst();
4.E pollLast();
5.E ceiling(E e);
6.E floor(E e);
7.E higher(E e)
8.E lower(E e)
9.NavigableSet subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive);

练习:
键盘录入5个学生信息 (姓名,语文成绩,数学成绩,英语成绩),并按照指定的格式,总分从高到低,输出到文件

2.Map及其子类

Map接口概述

在这里插入图片描述

  • 将键映射到值的对象。(我们可以根据键快速地查找到值)
  • Map 中键是唯一的 (不能包含重复的键)
  • 每个键最多只能映射到一个值

Map接口API

在这里插入图片描述

  • V get(Object key):返回指定key所对应的value:如果此Map中不包呈该key,则返回null
public class project02 {
    public static void main(String[] args) {
        HashMap<String, Integer> map = new HashMap<>();
        map.put("赵丽颖", 17);
        map.put("杨幂", 18);
        map.put("林志玲", 19);
        map.put("小龙女", 20);
        System.out.println(map);
        System.out.println("关于get的验证:");
        Integer v1 = map.get("赵丽颖");
        Integer v2 = map.get("小蝴蝶");
        System.out.println("v1:"+v1);
        System.out.println("v2:"+v2);
        System.out.println(map);
        System.out.println("关于containsKey的验证:");
        boolean b1 = map.containsKey("赵丽颖");
        boolean b2 = map.containsKey("小蝴蝶");
        System.out.println("b1:"+b1);
        System.out.println("b2:"+b2);
        System.out.println(map);
    }
}

结果输出:

{林志玲=19, 赵丽颖=17, 小龙女=20, 杨幂=18}
关于get的验证:
v1:17
v2:null
{林志玲=19, 赵丽颖=17, 小龙女=20, 杨幂=18}
关于containsKey的验证:
b1:true
b2:false
{林志玲=19, 赵丽颖=17, 小龙女=20, 杨幂=18}
  • V put(K key,V value):添加一个key-value对,如果当前Map中已有一个与该key相等的key-value对,则新的key-value对会覆盖原来的key-value对,返回被替换的value值。若key不重复,返回null
public class project01 {
    public static void main(String[] args) {
        HashMap<String, String> map = new HashMap<>();

        String v1=map.put("李晨","范冰冰1");
        System.out.println("v1:"+v1);
        System.out.println(map);

        String v2=map.put("李晨","范冰冰2");
        System.out.println("v2:"+v2);
        System.out.println(map);

        String v3=map.put("杨过","小龙女");
        String v4=map.put("高富帅","小龙女");
        System.out.println(map);

    }
}

结果输出:

v1:null
{李晨=范冰冰1}
v2:范冰冰1
{李晨=范冰冰2}
{高富帅=小龙女, 杨过=小龙女, 李晨=范冰冰2}
  • V remove(Object key):删除指定key所对应的key-value对,返回被删除key所关联的value,如果该key不存在,则返回null
public class project02 {
    public static void main(String[] args) {
        HashMap<String, Integer> map = new HashMap<>();
        map.put("赵丽颖", 17);
        map.put("杨幂", 18);
        map.put("林志玲", 19);
        map.put("小龙女", 20);
        System.out.println(map);
        Integer v1 = map.remove("赵丽颖");
        Integer v2 = map.remove("小蝴蝶");
        System.out.println("v1:"+v1);
        System.out.println("v2:"+v2);
        System.out.println(map);
    }
}

结果输出:

{林志玲=19, 赵丽颖=17, 小龙女=20, 杨幂=18}
v1:17
v2:null
{林志玲=19, 小龙女=20, 杨幂=18}
  • void clear()
  • boolean containsKey(Object key)
  • boolean containsValue(Object value)
  • boolean isEmpty()
  • int size()
  • Set keySet()
    在这里插入图片描述
/**
 * @Author:gaoyuan
 * @Description:map集合第一种遍历方式:通过键找值的方式 实现步骤;
 * 1.使用map集合中的方法keyset(),把map集合所有的key取出来,存储到一个set集合中
 * 2.遍历set集合,获取map集合中的每一个key
 * 3.通过map集合中的方法get(key),通过key找到value
 * @DateTime:2021/2/13 16:23
 **/
public class project03 {
    public static void main(String[] args) {
        HashMap<String, Integer> map = new HashMap<>();
        map.put("赵丽颖", 17);
        map.put("杨幂", 18);
        map.put("林志玲", 19);
        map.put("小龙女", 20);
        System.out.println(map);
        System.out.println("关于keyset的验证:");
        // 1.使用map集合中的方法keyset(),把map集合所有的key取出来,存储到一个set集合中
        Set<String> set = map.keySet();
        //2.遍历set集合,获取map集合中的每一个key
        Iterator<String> it = set.iterator();
        //3.通过map集合中的方法get(key),通过key找到value
        while (it.hasNext()) {
            String key = it.next();
            Integer value = map.get(key);
            System.out.println(key + "=" + value);
        }

        System.out.println("______________________");
        for (String key : set) {
            Integer value = map.get(key);
            System.out.println(key + "=" + value);
        }
    }
}

结果输出:

{林志玲=19, 赵丽颖=17, 小龙女=20, 杨幂=18}
关于keyset的验证:
林志玲=19
赵丽颖=17
小龙女=20
杨幂=18
______________________
林志玲=19
赵丽颖=17
小龙女=20
杨幂=18
  • Collection values()
  • Set<Map.Entry<K,V>> entrySet()
    在这里插入图片描述
/**
 * @Author:gaoyuan
 * @Description:map集合第二种遍历方式:把map集合中多个Entry对象取出来,存储到一个set集合中 实现步骤;
 * 1.使用map集合中的方法entrySet(),把map集合中多个entry对象取出来,存储到一个set集合中
 * 2.遍历set集合,获取map集合中的每一个entry对象
 * 3.通过map集合中的方法getKey()和getValue()获取键与值
 * @DateTime:2021/2/13 16:23
 **/
public class project04 {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("赵丽颖", 17);
        map.put("杨幂", 18);
        map.put("林志玲", 19);
        map.put("小龙女", 20);
        System.out.println(map);
        System.out.println("关于getKey()的验证:");
        //1.使用map集合中的方法entrySet(),把map集合中多个entry对象取出来,存储到一个set集合中
        Set<Map.Entry<String, Integer>> set = map.entrySet();

        Iterator<Map.Entry<String, Integer>> it = set.iterator();

        while (it.hasNext()) {
            Map.Entry<String, Integer> next = it.next();
            String key = next.getKey();
            Integer value = next.getValue();
            System.out.println(key + "=" + value);
        }

        System.out.println("-------------------" );
for (Map.Entry<String,Integer> entry:set){
    String key = entry.getKey();
    Integer value = entry.getValue();
    System.out.println(key + "=" + value);
}

    }
}

结果输出:

{林志玲=19, 赵丽颖=17, 小龙女=20, 杨幂=18}
关于getKey()的验证:
林志玲=19
赵丽颖=17
小龙女=20
杨幂=18
-------------------
林志玲=19
赵丽颖=17
小龙女=20
杨幂=18

Map集合的获取功能

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Map集合的遍历

方式一

在这里插入图片描述
在这里插入图片描述

方式二

在这里插入图片描述
在这里插入图片描述

案例:ArrayList集合存储HashMap元素并遍历

在这里插入图片描述

public class ArrayListIncludeHashMapDemo {
    public static void main(String[] args) {
        //创建ArrayList集合
        ArrayList<HashMap<String, String>> array = new ArrayList<HashMap<String, String>>();

        //创建HashMap集合,并添加键值对元素
        HashMap<String, String> hm1 = new HashMap<String, String>();
        hm1.put("孙策", "大乔");
        hm1.put("周瑜", "小乔");
        //把HashMap作为元素添加到ArrayList集合
        array.add(hm1);

        HashMap<String, String> hm2 = new HashMap<String, String>();
        hm2.put("郭靖", "黄蓉");
        hm2.put("杨过", "小龙女");
        //把HashMap作为元素添加到ArrayList集合
        array.add(hm2);

        HashMap<String, String> hm3 = new HashMap<String, String>();
        hm3.put("令狐冲", "任盈盈");
        hm3.put("林平之", "岳灵珊");
        //把HashMap作为元素添加到ArrayList集合
        array.add(hm3);

        //遍历ArrayList集合
        for (HashMap<String, String> hm : array) {
            Set<String> keySet = hm.keySet();
            for (String key : keySet) {
                String value = hm.get(key);
                System.out.println(key + "," + value);
            }
        }
    }
}

在这里插入图片描述

案例:HashMap集合存储ArrayList元素并遍历

在这里插入图片描述

public class HashMapIncludeArrayListDemo {
    public static void main(String[] args) {
        //创建HashMap集合
        HashMap<String, ArrayList<String>> hm = new HashMap<String, ArrayList<String>>();

        //创建ArrayList集合,并添加元素
        ArrayList<String> sgyy = new ArrayList<String>();
        sgyy.add("诸葛亮");
        sgyy.add("赵云");
        //把ArrayList作为元素添加到HashMap集合
        hm.put("三国演义",sgyy);

        ArrayList<String> xyj = new ArrayList<String>();
        xyj.add("唐僧");
        xyj.add("孙悟空");
        //把ArrayList作为元素添加到HashMap集合
        hm.put("西游记",xyj);

        ArrayList<String> shz = new ArrayList<String>();
        shz.add("武松");
        shz.add("鲁智深");
        //把ArrayList作为元素添加到HashMap集合
        hm.put("水浒传",shz);

        //遍历HashMap集合
        Set<String> keySet = hm.keySet();
        for(String key : keySet) {
            System.out.println(key);
            ArrayList<String> value = hm.get(key);
            for(String s : value) {
                System.out.println("\t" + s);
            }
        }
    }
}

在这里插入图片描述

案例:统计字符串中每个字符出现的次数

在这里插入图片描述

public class HashMapDemo {
    public static void main(String[] args) {
        //键盘录入一个字符串
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入一个字符串:");
        String line = sc.nextLine();

        //创建HashMap集合,键是Character,值是Integer
//        HashMap<Character, Integer> hm = new HashMap<Character, Integer>();
        TreeMap<Character, Integer> hm = new TreeMap<Character, Integer>();

        //遍历字符串,得到每一个字符
        for (int i = 0; i < line.length(); i++) {
            char key = line.charAt(i);

            //拿得到的每一个字符作为键到HashMap集合中去找对应的值,看其返回值
            Integer value = hm.get(key);

            if (value == null) {
                //如果返回值是null:说明该字符在HashMap集合中不存在,就把该字符作为键,1作为值存储
                hm.put(key,1);
            } else {
                //如果返回值不是null:说明该字符在HashMap集合中存在,把该值加1,然后重新存储该字符和对应的值
                value++;
                hm.put(key,value);
            }
        }

        //遍历HashMap集合,得到键和值,按照要求进行拼接
        StringBuilder sb = new StringBuilder();

        Set<Character> keySet = hm.keySet();
        for(Character key : keySet) {
            Integer value = hm.get(key);
            sb.append(key).append("(").append(value).append(")");
        }

        String result = sb.toString();

        //输出结果
        System.out.println(result);
    }
}

在这里插入图片描述

HashMap概述

  • 基于哈希表的Map接口实现
  • 允许null键和null值
  • 不保证映射的顺序,特别是它不保证该顺序恒久不变
  • 不同步

存储自定义类型键值

  • Map集合保证key是唯一的:
  • 作为key的元素,必须重写hashcode方法和equals方法,以保证key唯一
    Person.java(还未重写hashcode方法和equals)
public class Person {
    private String name;
     int age;

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

    public Person() {
    }



    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 +
                '}';
    }

HashMapDemo.java

/**
 * @Author:gaoyuan
 * @Description:HashMap存储自定义类型键值
 * Map集合保证key是唯一的:
 * 作为key的元素,必须重写hashcode方法和equals方法,以保证key唯一
 * @DateTime:2021/2/13 21:24
 **/
public class HashMapDemo {
    public static void main(String[] args) {
        show01();
    }


    /**
     *
     * HashMap存储自定义类型键值
     *key:string类,重写hashcode方法和equals方法,可保证key唯一
     *value:Interge类,可重复
     */
    private static void show01(){
        HashMap<String, Person> map = new HashMap<>();
        map.put("北京",new Person("大娃",100));//key值与后面一样,新的value值覆盖前面的value值。重写了hashcode与equals
        map.put("成都",new Person("二娃",99));
        map.put("南京",new Person("三娃",98));
        map.put("北京",new Person("四娃",97));
        System.out.println(map);
//使用keyset+增强for遍历map集合
        Set<String> set = map.keySet();
        for (String key:set){
            Person value = map.get(key);
            System.out.println(key+"-->"+value);
        }



        HashMap<Person,String> map2 = new HashMap<>();
        //往集合中添加元素
        map2.put(new Person("女王",18),"英国");//没有重写hashcode方法和equals方法,key不唯一
        map2.put(new Person("秦始皇",19),"秦国");
        map2.put(new Person("普京",20),"俄罗斯");
        map2.put(new Person("女王",18),"漂亮国");
        Set<Map.Entry<Person, String>> entries = map2.entrySet();
        for (Map.Entry<Person,String> entrie:entries){
            Person key = entrie.getKey();
            String value = entrie.getValue();
            System.out.println(key + "=" + value);
        }
    }
}

结果输出:女王出现重复

{成都=Person{name='二娃', age=99}, 北京=Person{name='四娃', age=97}, 南京=Person{name='三娃', age=98}}
成都-->Person{name='二娃', age=99}
北京-->Person{name='四娃', age=97}
南京-->Person{name='三娃', age=98}
Person{name='普京', age=20}=俄罗斯
Person{name='秦始皇', age=19}=秦国
Person{name='女王', age=18}=英国
Person{name='女王', age=18}=漂亮国

Person重写后

public class Person {
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return getAge() == person.getAge() &&
                Objects.equals(getName(), person.getName());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getName(), getAge());
    }

    private String name;
    private int age;

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

    public Person() {
    }



    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 +
                '}';
    }

}

结果输出:

{成都=Person{name='二娃', age=99}, 北京=Person{name='四娃', age=97}, 南京=Person{name='三娃', age=98}}
成都-->Person{name='二娃', age=99}
北京-->Person{name='四娃', age=97}
南京-->Person{name='三娃', age=98}
Person{name='女王', age=18}=漂亮国
Person{name='秦始皇', age=19}=秦国
Person{name='普京', age=20}=俄罗斯

LinkedHashMap概述

  • HashMap的子类
  • Map 接口的哈希表和链表实现,具有可预知的迭代顺序
  • 链表定义了迭代顺序,该迭代顺序就是键值对的插入顺序
  • 不同步
public class project05 {
    public static void main(String[] args) {
        HashMap<String, String> map = new HashMap<>();
        map.put("a","a");
        map.put("c","c");
        map.put("b","b");
        map.put("a","z");
        System.out.println("HashMap是无序输出:"+map);

        LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>();
        linkedHashMap.put("a","a");
        linkedHashMap.put("c","c");
        linkedHashMap.put("b","b");
        linkedHashMap.put("a","z");
        System.out.println("LinkedHashMap是有序输出:"+linkedHashMap);

    }
}

结果输出:

HashMap是无序输出:{a=z, b=b, c=c}
LinkedHashMap是有序输出:{a=z, c=c, b=b}

TreeMap概述

  • 底层的数据结构是红黑树
  • 如果创建对象时,传入了 Comparator 对象,键将按 Comparator 进行排序,否则键将按自然顺序进行排序。
  • 不同步

除了Map接口中定义的方法外,由于TreeMap中的键是大小有序的,因此它还有一些特殊的方法。
1.K firstKey()
2.K lastKey()
3.Map.Entry<K, V> pollFirstEntry();
4.Map.Entry<K,V> pollLastEntry();
5.K floorKey(K key)
6.K ceilingKey(K key)
7.K lowerKey(K key)
8.K higherKey(K key)
9.NavigableMap<K, V> submap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive )

Properties概述

  • Hashtable<Object, Object> 的子类
  • Properties 类表示了一个可持久的属性集
  • Properties 可保存在流中或从流中加载
  • Properties 中每个键及其对应值都是一个字符串

注意事项:不要使用Hashtable里面定义的方法添加键值对!因为它们可以插入不是String 类型的数据。

如果一个Properties中含有非String的键值对,那么这样的Properties是”不安全”的。调用 store 或者 save 方法将失败

Properties 的 API:

  • String getProperty(String key)
  • String getProperty(String key, String defaultValue)
  • Object setProperty(String key, String value)
  • Set stringPropertyNames();
  • void store(OutputStream out, String comments)
  • void store(Writer out, String comments)
  • void load(InputStream inStream)
  • void load(Reader read)

注意事项:字节流默认使用 ISO 8859-1 字符编码。
练习:

  1. “aababcabcdabcde”,获取字符串中每一个字母出现的次数要求结果:a(5)b(4)c(3)d(2)e(1)

  2. 给定一个整数数组和一个目标值,找出数组中和为目标值的两个数, 返回它们的索引。
    你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。

比如:nums = [2, 7, 11, 15], target = 9.
因为 nums[0] + nums[1] = 2 + 7 = 9. 所以返回 [0, 1].

  1. 请设计一个猜数字小游戏,可以试玩5次。试玩结束之后,给出提示:游戏试玩结束,
    请付费。

Map 小结:
在这里插入图片描述

3.JDK1.5新特性

在这里插入图片描述

(1)泛型的引入及好处:

a. 提高了程序的安全性
b. 将运行期遇到的问题转移到了编译期
c. 省去了类型强转的麻烦

/*
    需求:Collection集合存储字符串并遍历
 */
public class GenericDemo {
    public static void main(String[] args) {
        //创建集合对象
//        Collection c = new ArrayList();
        //提前将集合类型设定为<String>类型,将运行期可能遇到的问题转移到了编译期
        Collection<String> c = new ArrayList<String>();

        //添加元素
        c.add("hello");
        c.add("world");
        c.add("java");
//        c.add(100);

        //遍历集合
//        Iterator it = c.iterator();
        Iterator<String> it = c.iterator();//用.var,自动变为Iterator<String>
        while (it.hasNext()) {
//            Object obj = it.next();//刚开始没有指定类型,用Object表示类型
//            System.out.println(obj);
//            String s = (String)it.next(); //ClassCastException
            String s = it.next();//泛型指定类型后,不用强转类型
            System.out.println(s);
        }
    }
}

结果输出:

hello
world
java

泛型应用

泛型类(把泛型定义在类上)

格式:public class 类名<泛型类型1,…>
注意:参数化类型必须是引用类型
示例1

Student.java

public class Student {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Teacher.java

public class Teacher {
    private Integer age;

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

Generic.java

public class Generic<T>{
    private T t;//类型为T类型

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}

GenericDemo.java

/*
    测试类
 */
public class GenericDemo {
    public static void main(String[] args) {
        Student s = new Student();
        s.setName("林青霞");
        System.out.println(s.getName());

        Teacher t = new Teacher();
        t.setAge(30);
//        t.setAge("30");
        System.out.println(t.getAge());
        System.out.println("--------");

        Generic<String> g1 = new Generic<String>();//创建一个String类型
        g1.setT("林青霞");
        System.out.println(g1.getT());

        Generic<Integer> g2 = new Generic<Integer>();//创建一个Integer类型
        g2.setT(30);
        System.out.println(g2.getT());

        Generic<Boolean> g3 = new Generic<Boolean>();//创建一个Boolean类型
        g3.setT(true);
        System.out.println(g3.getT());
    }
}

结果输出:

林青霞
30
--------
林青霞
30
true

示例2
方法的一般调用:
Generic.java

public class Generic {
 public void show(String s){
     System.out.println(s);
 }
    public void show(Integer i){
        System.out.println(i);
    }
    public void show(Boolean b){
        System.out.println(b);
    }
}

GenericDemo.java

public class GenericDemo {
    public static void main(String[] args) {
        Generic g = new Generic();
        g.show("林青霞");
        g.show(30);
        g.show(true);
    }}

结果输出;

林青霞
30
true

用泛型类改进:(方法代码较简介,测试类中代码多次创建,较复杂)

Generic.java

public class Generic<T> {
    public void show(T t){
        System.out.println(t);
    }
}

GenericDemo.java

public class GenericDemo {
    public static void main(String[] args) {
       Generic<String> g1 = new Generic<String>();
        g1.show("林青霞");
        Generic<Integer> g2 = new Generic<Integer>();
        g2.show(30);
        Generic<Boolean> g3 = new Generic<Boolean>();
        g3.show(true);
    }
}

结果输出;

林青霞
30
true

泛型方法:(把泛型定义在方法上)

格式:public <泛型类型> 返回类型 方法名(泛型类型 .)
示例2
用泛型方法改进:(方法代码和测试类代码都较简介)
在这里插入图片描述

Generic.java

public class Generic {
    public <T> void show(T t) {
        System.out.println(t);
    }
}

GenericDemo.java

public class GenericDemo {
    public static void main(String[] args) {
       Generic g = new Generic();
        g.show("林青霞");
        g.show(30);
        g.show(true);
        g.show(23.676);
    }
}

结果输出;

林青霞
30
true
23.676

泛型接口:(把泛型定义在接口上)

格式:public interface 接口名<泛型类型1…>
Generic.java(接口)

public interface Generic<T> {
void show(T t);
}

GenericImpl.java(接口的实现类)

public class GenericImpl<T> implements Generic<T> {
    @Override
    public void show(T t) {
        System.out.println(t);
    }
}

GenericDemo.java

public class GenericDemo {
    public static void main(String[] args) {
        GenericImpl<String> g1 = new GenericImpl<String>();
        g1.show("林青霞");

        GenericImpl<Integer> g2 = new GenericImpl<Integer>();
        g2.show(30);
    }
}

结果输出;

林青霞
30

泛型通配符

在这里插入图片描述

① 泛型通配符<?>
任意类型,如果没有明确,那么就是Object以及任意的Java类
②? extends E
向下限定,E及其子类
③ ? super E
向上限定,E及其父类
GenericDemo.java
在这里插入图片描述

public class GenericDemo {
    public static void main(String[] args) {
        //父子类关系Object>Number>Integer
        List<?> list1 = new ArrayList<Object>();
        List<?> list2 = new ArrayList<Number>();
        List<?> list3 = new ArrayList<Integer>();
        System.out.println("------------");

        //? extends E
        //类型通配符上限,向下拓展,E及其子类
        //List<? extends Number> list4 = new ArrayList<Object>();//此行报错,Object是Number的父类
        List<? extends Number> list5 = new ArrayList<Number>();
        List<? extends Number> list6 = new ArrayList<Integer>();

        //? super E
        //类型通配符下限,向上拓展,E及其父类
        List<? super Number> list7 = new ArrayList<Object>();
        List<? super Number> list8 = new ArrayList<Number>();
        //List<? super Number> list9 = new ArrayList<Integer>();//此行报错,Integer是Number的子类
    }
}

泛型的注意事项

1.泛型 jdk 1.5:
List list = new ArrayList();
jdk1.7 改进:
List list = new ArrayList<>();

List list = new ArrayList ();
没有传泛型 --> 如果我们设计一个类,( ArrayList)他是需要泛型的。但是, 在创建对象的时候, 我们又没有给定泛型, 那么对于这个被创建的对象, 它的泛型到底是什么那? 是Object类型替代

2.泛型一般用于哪些地方?
集合类, 数据容器

3.泛型方法
使用泛型的方法, 并不一定是泛型方法
泛型方法: 在方法上定义了泛型的方法

4.泛型必须使用引用类型
泛型不能使用基本类型.
集合类: 集合类之中是带有泛型的设计:
a. 只能存储引用数据类型(实际上是泛型特点一个延伸)
b. 可以自动地调整自己的大小

5.泛型的参数写法
T: type 类型
E: element 元素
K: key 键值对的键
V: value 键值对的值

6.泛型的擦除

所有带泛型的代码, 在编译完成之后, 泛型完全就不存在了, 全部替换成Object泛型仅存在于编译之前

(2)增强for循环(foreach)

在这里插入图片描述

增强for概述:简化数组和Collection集合的遍历
格式
for(元素数据类型 变量 : 数组或者Collection集合) {
使用变量即可,该变量就是元素
}

好处:简化遍历

注意事项:增强for的目标要判断是否为null

import java.util.ArrayList;
import java.util.List;

/*
    增强for:简化数组和Collection集合的遍历
        实现Iterable接口的类允许其对象成为增强型 for语句的目标
        它是JDK5之后出现的,其内部原理是一个Iterator迭代器

    格式:
        for(元素数据类型 变量名 : 数组或者Collection集合) {
            //在此处使用变量即可,该变量就是元素
        }
 */
public class ForDemo {
    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5};
        for(int i : arr) {
            System.out.println(i);
        }
        System.out.println("--------");

        String[] strArray = {"hello","world","java"};
        for(String s : strArray) {
            System.out.println(s);
        }
        System.out.println("--------");

        List<String> list = new ArrayList<String>();
        list.add("hello");
        list.add("world");
        list.add("java");

        for(String s : list) {
            System.out.println(s);
        }
        System.out.println("--------");

        //内部原理是一个Iterator迭代器
        /*
        for(String s : list) {
            if(s.equals("world")) {
                list.add("javaee"); //ConcurrentModificationException
            }
        }
        */
    }
}

在这里插入图片描述

(3)可变长参数

在这里插入图片描述

可变参数概述:定义方法的时候不知道该定义多少个参数,我们就可以使用可变长参数。
格式
修饰符 返回值类型 方法名(数据类型… 变量名){}
注意:
1.这里的变量其实是一个数组
2.可变长参数只能位于最后
3.一个方法不能有多个可变长参数
示例;

用可变参数实现加法操作

public class ArgsDemo01 {
    public static void main(String[] args) {
        System.out.println(sum(10, 20));
        System.out.println(sum(10, 20, 30));
        System.out.println(sum(10, 20, 30, 40));

        System.out.println(sum(10, 20, 30, 40, 50));
        System.out.println(sum(10, 20, 30, 40, 50, 60));
        System.out.println(sum(10, 20, 30, 40, 50, 60, 70));
        System.out.println(sum(10, 20, 30, 40, 50, 60, 70, 80, 90, 100));

    }

    public static int sum(int... a) {
        int sum = 0;
        for (int i : a) {
            sum += i;
        }
        return sum;
    }

}

结果输出:

20
50
90
140
200
270
540

(4)可变长参数的使用:

在这里插入图片描述

Arrays工具类中有一个静态方法

  • public static List asList​(T… a):返回由指定数组支持的固定大小的列表
  • 返回的集合只能做修改操作
    示例
public class ArgsDemo02 {
    public static void main(String[] args) {
        List<String> list= Arrays.asList("hello","world","java");

        //list.add("javase");//UnsupportedOperationException,因为添加改变了原数组的长度
       // list.remove("hello");//UnsupportedOperationException,因为删除改变了原数组的长度
        list.set(1,"FSGDvc");//修改元素不改变数组的大小
        System.out.println(list);
    }
}

结果输出:

[hello, FSGDvc, java]

List接口中有一个静态方法

  • public static List of​(E… elements):返回包含任意数量元素不可变列表
  • 返回的集合不能做增删改操作
    示例
public class ArgsDemo03 {
    public static void main(String[] args) {
                List<String> list = List.of("hello", "world", "java", "world");

        list.add("javaee");//UnsupportedOperationException
        list.remove("java");//UnsupportedOperationException
        list.set(1,"javaee");//UnsupportedOperationException

        System.out.println(list);
    }
}

结果输出:

hello
world
java
world

Set接口中有一个静态方法

  • public static Set of​(E… elements) :返回一个包含任意数量元素不可变集合
  • 在给元素的时候,不能重复。
  • 返回的集合不能做增删操作没有修改的方法
    示例:
public class ArgsDemo03 {
    public static void main(String[] args) {
                List<String> list = List.of("hello", "world", "java", "world");//IllegalArgumentException

        list.add("javaee");//UnsupportedOperationException
        list.remove("java");//UnsupportedOperationException
        list.set(1,"javaee");

        System.out.println(list);
    }
}

4.Collections

在这里插入图片描述

public class CollectionsDemo01 {
    public static void main(String[] args) {
        //创建集合对象
        List<Integer> list = new ArrayList<Integer>();

        //添加元素
        list.add(30);
        list.add(20);
        list.add(50);
        list.add(10);
        list.add(40);

        //public static <T extends Comparable<? super T>> void sort​(List<T> list):将指定的列表按升序排序
        Collections.sort(list);
        System.out.println(list);
        //public static void reverse​(List<?> list):反转指定列表中元素的顺序
        Collections.reverse(list);
        System.out.println(list);
        //public static void shuffle​(List<?> list):使用默认的随机源随机排列指定的列表
        Collections.shuffle(list);

        System.out.println(list);
    }
}

在这里插入图片描述

案例:ArrayList集合存储学生并排序

在这里插入图片描述

Student

public class Student {
    private String name;
    private int age;

    public Student() {
    }

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

    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;
    }
}

public class CollectionsDemo02 {
    public static void main(String[] args) {
        //创建ArrayList集合对象
        ArrayList<Student> array = new ArrayList<Student>();

        //创建学生对象
        Student s1 = new Student("linqingxia", 30);
        Student s2 = new Student("zhangmanyu", 35);
        Student s3 = new Student("wangzuxian", 33);
        Student s4 = new Student("liuyan", 33);

        //把学生添加到集合
        array.add(s1);
        array.add(s2);
        array.add(s3);
        array.add(s4);

        //使用Collections对ArrayList集合排序
        //sort​(List<T> list, Comparator<? super T> c)
        Collections.sort(array, new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                //按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
                int num = s1.getAge() - s2.getAge();
                int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
                return num2;
            }
        });

        //遍历集合
        for (Student s : array) {
            System.out.println(s.getName() + "," + s.getAge());
        }

    }
}

在这里插入图片描述

5.数组和链表

1.数组

数组的长度是固定的
数组只能存储同一种数据类型的元素

注意:在Java中只有一维数组的内存空间是连续多维数组的内存空间不一定连续

那么数组又是如何实现随机访问的呢?
寻址公式:i_address = base_address + i * type_length
为什么数组的效率比链表高?
数组是连续存储, 链表是非连续存储
数组的基本操作

添加 (保证元素的顺序)
最好情况:O(1)
最坏情况:移动n个元素,O(n)
平均情况:移动 n/2 个元素,O(n)

删除 (保证元素的顺序)
最好情况:O(1)
最坏情况:移动n-1个元素,O(n)
平均情况:移动(n-1)/2个元素,O(n)

查找
a. 根据索引查找元素:O(1)
b. 查找数组中与特定值相等的元素
①大小无序:O(n)
②大小有序:O(log2n)

总结: 数组增删慢,查找快。

2.链表

形象地说,链表就是用一串链子将结点串联起来。

结点:包含数据域和指针域。
数据域:数据
指针域:下一个结点的地址
在这里插入图片描述
在这里插入图片描述
链表及循环链表示例:
Node.java

public class Node {
    String value;
    Node next;

    public Node(String value, Node next) {
        this.value = value;
        this.next = next;
    }
}

LinkedDemo01.java

public class LinkedDemo01 {
    public static void main(String[] args) {
        Node node1 = new Node("20", null);
        Node node2 = new Node("40", node1);
        Node node3 = new Node("70", node2);
        node1.next=node3;//创建循环链表
        System.out.println("输出节点node1的next节点node3的值:"+node1.next.value);
        //

        //node3->node2->node1
        //node3.next.next等价于->node1
        System.out.println("输出节点node3的next的next节点的地址:"+node3.next.next.next);

      Node nexttest=  node2.next;//nexttest即为node1的地址对象
        System.out.println("输出nexttest即为node1的地址对象:"+nexttest);

        String str="zs";
        System.out.println(str);

    }
}

结果输出:

输出节点node1的next节点node3的值:70
输出节点node3的next的next节点的地址:com.ketang.day28_20210131.Node@1b6d3586
输出nexttest即为node1的地址对象:com.ketang.day28_20210131.Node@4554617c
zs

双向链表及双向循环链表示例:
DBNode.java

public class DBNode {
    String value;
    DBNode pre;
    DBNode next;

    public DBNode(String value, DBNode pre, DBNode next) {
        this.value = value;
        this.pre = pre;
        this.next = next;
    }
}

DBNodeDemo.java

public class DBNodeDemo {
    public static void main(String[] args) {
        DBNode dbNode1 = new DBNode("1", null, null);
        DBNode dbNode2 = new DBNode("2", dbNode1, null);
        DBNode dbNode3 = new DBNode("3", dbNode2, null);
        DBNode dbNode4 = new DBNode("4", dbNode3, null);
//  //实现1-->2-->3-->4的双向链表
//       //     1<--2<--3<--4
        dbNode1.next=dbNode2;
        dbNode2.next=dbNode3;
        dbNode3.next=dbNode4;

        System.out.println(dbNode3.pre.pre.value);
//实现双向循环链表
        dbNode1.pre=dbNode4;
        dbNode4.next=dbNode1;
        System.out.println(dbNode4.next.value);
        System.out.println(dbNode1.pre.value);

    }
}

DBNodeDemo.java

class DBNodeDemo {
    public static void main(String[] args) {
        DBNode dbNode1 = new DBNode("1", null, null);
        DBNode dbNode2 = new DBNode("2", dbNode1, null);
        DBNode dbNode3 = new DBNode("3", dbNode2, null);
        DBNode dbNode4 = new DBNode("4", dbNode3, null);
//  //实现1-->2-->3-->4的双向链表
//       //     1<--2<--3<--4
        dbNode1.next=dbNode2;
        dbNode2.next=dbNode3;
        dbNode3.next=dbNode4;

        System.out.println("输出dbNode3节点pre的pre节点dbNode1的值:"+dbNode3.pre.pre.value);
//实现双向循环链表
        dbNode1.pre=dbNode4;
        dbNode4.next=dbNode1;
        System.out.println("输出dbNode4节点next节点dbNode1的值:"+dbNode4.next.value);
        System.out.println("输出dbNode1节点pre节点dbNode4的值:"+dbNode1.pre.value);

    }
}

结果输出:

输出dbNode3节点pre的pre节点dbNode1的值:1
输出dbNode4节点next节点dbNode1的值:1
输出dbNode1节点pre节点dbNode4的值:4

循环链表我们用的一般比较少,但是当处理的数据具有环形结构时,就特别适合用循环链表,比如约瑟夫问题。

单链表

增加(在某个结点后面添加)

 //添加方法
    public boolean add(String str) {
        //判断链表是否为空
        if (top == null || size == 0) {
            //代表一个空链表
            top = new Node(str, null);//创建头节点
            size++;//长度加一
            return true;
        }
//查找尾节点
        Node mid = top;
        while (mid.next != null) {
            mid = mid.next;
        }

        //上述循环的跳出条件,就是mid是尾节点
        mid.next = new Node(str, null);//从尾节点开始添加节点
        size++;
        return true;

    }

删除(在某个结点后面删除)

 //删除方法
    public String delete(String str) {
        // TODO:参数验证
        if (top == null || size == 0) throw new RuntimeException("linked is null");
        //如果要删除的是头结点
        if (top.value.equals(str)) {
            //要删除的节点是头结点
            top = top.next;
            size--;
            return str;
        }
        //如果要删除的不是头结点
        Node mid = top;
        while (mid.next != null && !mid.next.value.equals(str)) {//一直向后查找
            mid = mid.next;
        }

        //意味着
        //1.mid.next=null,没有删除的元素
        //2.mid,next就是要删除的点
        if (mid.next == null) {
            //没找到
            return null;
        }
//mid.next要找的节点
        mid.next = mid.next.next;//覆盖删除
        size--;
        return str;
    }

修改:

//修改方法
    public boolean set(String oldStr, String newStr) {
//TODO:参数验证:
        if (top == null || size == 0) throw new RuntimeException("likend is null");
        //修改是否是头结点
        if (top.value.equals(oldStr)) {
            //就是要修改头结点
            top.value = newStr;
            return true;
        }

        //修改的不是头结点,一直向下寻找
        Node mid = top;
        //查找要替换的元素
        while (mid.next != null && !mid.next.value.equals(oldStr)) {
            mid = mid.next;
        }
//没找到
        if (mid.next == null) {
            return false;
        }
//必然找到
        mid.next.value = newStr;
        return true;
    }

查找:
a. 根据索引查找元素
b. 查找链表中与特定值相等的元素
①元素大小有序
②元素大小无序
总结:链表增删快,查找慢。

单链表增删改示例
MyLinked.java

public class MyLinked {
    //单链表
    Node top;//头结点
    int size;

    @Override
    public String toString() {
        return "MyLinked{" +
                "top=" + top +
                ", size=" + size +
                '}';
    }

    //添加方法
    public boolean add(String str) {
        //判断链表是否为空
        if (top == null || size == 0) {
            //代表一个空链表
            top = new Node(str, null);//创建头节点
            size++;//长度加一
            return true;
        }
//查找尾节点
        Node mid = top;
        while (mid.next != null) {
            mid = mid.next;
        }

        //上述循环的跳出条件,就是mid是尾节点
        mid.next = new Node(str, null);//从尾节点开始添加节点
        size++;
        return true;

    }

    //删除方法
    public String delete(String str) {
        // TODO:参数验证
        if (top == null || size == 0) throw new RuntimeException("linked is null");
        //如果要删除的是头结点
        if (top.value.equals(str)) {
            //要删除的节点是头结点
            top = top.next;
            size--;
            return str;
        }
        //如果要删除的不是头结点
        Node mid = top;
        while (mid.next != null && !mid.next.value.equals(str)) {//一直向后查找
            mid = mid.next;
        }

        //意味着
        //1.mid.next=null,没有删除的元素
        //2.mid,next就是要删除的点
        if (mid.next == null) {
            //没找到
            return null;
        }
//mid.next要找的节点
        mid.next = mid.next.next;//覆盖删除
        size--;
        return str;
    }

    //修改方法
    public boolean set(String oldStr, String newStr) {
//TODO:参数验证:
        if (top == null || size == 0) throw new RuntimeException("likend is null");
        //修改是否是头结点
        if (top.value.equals(oldStr)) {
            //就是要修改头结点
            top.value = newStr;
            return true;
        }

        //修改的不是头结点,一直向下寻找
        Node mid = top;
        //查找要替换的元素
        while (mid.next != null && !mid.next.value.equals(oldStr)) {
            mid = mid.next;
        }
//没找到
        if (mid.next == null) {
            return false;
        }
//必然找到
        mid.next.value = newStr;
        return true;
    }

    //查找方法
public boolean find(String str){
    //TODO:参数验证:
    if (top == null || size == 0) throw new RuntimeException("likend is null");
    //头结点是否是要查找的元素
    if (top.value.equals(str)) {
        return true;
    }

    Node mid=top;
    //查找指定的元素
    while (mid.next != null && !mid.next.value.equals(str)) {
        mid = mid.next;
    }
//没找到
    if (mid.next == null) {
        return false;
    }else//否则找到
        return true;
}


    class Node {
        String value;
        Node next;

        public Node(String value, Node next) {
            this.value = value;
            this.next = next;
        }

        @Override
        public String toString() {
            return "Node{" +
                    "value='" + value + '\'' +
                    ", next=" + next +
                    '}';
        }
    }
}

MyLinkedtest.java

public class MyLinkedtest {
    public static void main(String[] args) {
        MyLinked myLinked = new MyLinked();
        myLinked.add("dd");
        myLinked.add("gg");
        myLinked.add("ss");

        System.out.println(myLinked.toString());

        String gg = myLinked.delete("gg");
        System.out.println("删除gg后的链表:" + myLinked);

        myLinked.set("dd", "aa");
        myLinked.set("ss", "agrnzb");
        System.out.println("修改后的链表:" + myLinked);


        System.out.println("是否找到指定元素:" +myLinked.find("dd"));
        System.out.println("是否找到指定元素:" +myLinked.find("gg"));
        System.out.println("是否找到指定元素:" +myLinked.find("ss"));
        System.out.println("是否找到指定元素:" +myLinked.find("agrnzb"));

    }
}

结果输出;

MyLinked{top=Node{value='dd', next=Node{value='gg', next=Node{value='ss', next=null}}}, size=3}
删除gg后的链表:MyLinked{top=Node{value='dd', next=Node{value='ss', next=null}}, size=2}
修改后的链表:MyLinked{top=Node{value='aa', next=Node{value='agrnzb', next=null}}, size=2}
是否找到指定元素:false
是否找到指定元素:false
是否找到指定元素:false
是否找到指定元素:true

承接以上代码实现:(根据下标删除元素)

  /* 根据下标删除元素*/
    public boolean deleteindex(int index) {
        if (top == null || size == 0) throw new RuntimeException("likend is null");
        if (index >= size) throw new RuntimeException("越界异常:indexOutOfBoundsException!");

        Node mid = top;
        //如果节点为第一个节点
        if (index == 0) {
            System.out.println("第一个节点已删除!");
            top = top.next;//将头结点指向第二个节点
            size--;
            return true;
        }

        //找到删除节点的前一个节点
        int j = 0;
        while (mid.next != null && j < index - 1) {
            top = top.next;
            j++;
        }
        top.next = top.next.next;
        size--;
        return true;
    }

结果输出;

MyLinked{top=Node{value='dd', next=Node{value='gg', next=Node{value='ss', next=null}}}, size=3}
是否删除下标为1的元素:true
删除下标为1元素后的单链表:MyLinked{top=Node{value='dd', next=Node{value='ss', next=null}}, size=2}
Exception in thread "main" java.lang.RuntimeException: 越界异常:indexOutOfBoundsException!
	at com.ketang.day28_20210131.MyLinked.MyLinked.deleteindex(MyLinked.java:123)
	at com.ketang.day28_20210131.MyLinked.MyLinkedtest.main(MyLinkedtest.java:32)

双向链表
双向链表和单链表的时间复杂度是一样的,但双向链表有单链表没有的独特魅力——它有一条指向前驱结点的链接。(双向链表更常用)
增加 (在某个结点前面添加元素)
删除 (删除该结点)
查找
a. 查找前驱结点
b. 根据索引查找元素
c. 查找链表中与特定值相等的元素
① 元素大小无序
② 元素大小有序

    //查找方法
public boolean find(String str){
    //TODO:参数验证:
    if (top == null || size == 0) throw new RuntimeException("likend is null");
    //头结点是否是要查找的元素
    if (top.value.equals(str)) {
        return true;
    }

    Node mid=top;
    //查找指定的元素
    while (mid.next != null && !mid.next.value.equals(str)) {
        mid = mid.next;
    }
//没找到
    if (mid.next == null) {
        return false;
    }else//否则找到
        return true;
}

总结:虽然双向链表更占用内存空间,但是它在某些操作上的性能是优于单链表的。

思想:用空间换取时间

练习:

1.求链表的中间元素
解题思路
定义两个指针fast和slow。slow一次遍历一个节点,fast一次遍历两个节点,由于fast的速度是slow的两倍,所以当fast遍历完链表时,slow所处的节点就是链表的中间节点。

public class practise01 {
    public static void main(String[] args) {
        Node node1 = new Node("1", null);
        Node node2 = new Node("2", node1);
        Node node3 = new Node("3", node2);
        Node node4 = new Node("4", node3);
        Node node5 = new Node("5", node4);
        Node z=middleNode(node5);
        System.out.println(z);

    }

    public static Node middleNode(Node head) {
        if (head == null || head.next == null) {
            return head;
        }

        Node fast = head.next;//快指针
        Node low = head;//慢指针

        //快指针走两步,慢指针走一步
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            low = low.next;
        }
        
        return fast == null ? low : low.next;
    }
}

结果输出:

Node{value='3', next=Node{value='2', next=Node{value='1', next=null}}}

2.判断链表中是否有环(circle)

3.反转单链表

3.总结

在这里插入图片描述
1.数组是连续内存,CPU的高速缓存可预读数据(链表不能)。若数组过大无连续空间,会抛出OOM、
2.数组的缺点是大小固定。数组太大,浪费内存空间;数组太小,需重新申请更大数组,并将数据拷贝过去,耗时。
3.若对内存的使用苛刻,数组更适合。因结点有指针域,更耗内存。且对链表的频繁插入和删除,会导致结点对象的频繁创建和销毁,有可能会导致频繁的GC活动。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值