集合 Collection、数据结构、List、泛型深入等知识点

需要学会什么

  1. 集合概述体系、常用API 
  2. 集合遍历、存储对象
  3. 常用数据结构
  4. List系列集合
  5. 泛型深入

数组的特点

  1. 数组定义完成并启动后,类型确定、长度固定
  2. 适合元素的个数和类型确定的业务场景,不适合做需要增删数据操作

集合的特点

  1. 集合的大小不固定,启动后可以动态变化,类型也可以选择不固定。集合更像气球
  2. 集合非常适合做元素的增删操作

集合的代表

  1. Collection接口

Collection集合2大常用的集合体系

  1. List系列集合:添加的元素是有序、可重复、有索引
  2. Set系列集合:添加的元素是无序、不重复、无索引

注意:

  1. 集合支持泛型
  2. 集合和泛型不支持基本类型,只支持引用数据类型
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;

public class test {
    public static void main(String[] args) {
        // 有序 可重复 有索引
        Collection list=new ArrayList();
        list.add("1");
        list.add("2");
        list.add("3");
        System.out.println(list);
        //无序 不重复 不索引
        Collection L=new HashSet();
        L.add("a");
        L.add("b");
        L.add("c");
        System.out.println(L);
        // 集合和泛型不支持基本数据类型,只能支持引用数据类型
        Collection<String> Li=new ArrayList<>();
        Li.add("张三");
        Li.add("ad");
        System.out.println(Li);
        Collection<Integer> Ls=new ArrayList<>();
        Ls.add(123);
        Ls.add(456);
        System.out.println(Ls);
    }
}
输出
[1, 2, 3]
[a, b, c]
[张三, ad]
[123, 456]
Collection集合的常用API
- public boolean add(E e):  把给定的对象添加到当前集合中 。
- public void clear() :清空集合中所有的元素。
- public boolean remove(E e): 把给定的对象在当前集合中删除。
- public boolean contains(Object obj): 判断当前集合中是否包含给定的对象。
- public boolean isEmpty(): 判断当前集合是否为空。
- public int size(): 返回集合中元素的个数。
- public Object[] toArray(): 把集合中的元素,存储到数组中。
class test2 {
    public static void main(String[] args) {
        Collection <String > a=new ArrayList<>();
        a.add("zhangsan");
        System.out.println(a.add("李4"));
        a.add("123");
        a.add("123");
        //成功返回ture
        System.out.println(a);
        //清空集合元素
        //a.clear();
        System.out.println(a);
        //判断集合是否为空
        System.out.println(a.isEmpty()); //是为ture
        // 获取集合的大小
        System.out.println(a.size());
        //判断集合中是否有包含某个元素
        System.out.println(a.contains("zhangsan")); //ture
        //删除某个元素:如果有多个重复元素默认删除前面的第一个
        System.out.println(a.remove("123"));
        //把集合转成数组 [zhangsan, 李4, 123, 123]
        Object arr []=a.toArray();
        System.out.println("数组"+ Arrays.toString(arr));
        //把一个集合的元素全部倒入另一个人集合中
        Collection<String> c1 = new ArrayList<>();
        c1.add("java1");
        c1.add("java2");
        Collection<String> c2 = new ArrayList<>();
        c2.add("赵敏");
        c2.add("殷素素");
        // addAll把c2集合的元素全部倒入到c1中去。
        c1.addAll(c2);
        System.out.println(c1);
        System.out.println(c2);
Collection集合的遍历方式

什么是遍历? 为什么开发中要遍历?
    遍历就是一个一个的把容器中的元素访问一遍。
    开发中经常要统计元素的总和,找最值,找出某个数据然后干掉等等业务都需要遍历。
Collection集合的遍历方式是全部集合都可以直接使用的,所以我们学习它。
Collection集合的遍历方式有三种:
    (1)迭代器。
    (2)foreach(增强for循环)。
    (3)JDK 1.8开始之后的新技术Lambda表达式(了解)
迭代器遍历集合。
    -- 方法:
         public Iterator iterator(): 获取集合对应的迭代器,用来遍历集合中的元素的
         boolean hasNext():判断是否有下一个元素,有返回true ,反之。
         E next():获取下一个元素值!
    --流程:
        1.先获取当前集合的迭代器
            Iterator<String> it = lists.iterator();
        2.定义一个while循环,问一次取一次。
          通过it.hasNext()询问是否有下一个元素,有就通过
          it.next()取出下一个元素。

        如果越界会产生会出现NoSuchElementException异常

法1

public class test1 {
    public static void main(String[] args) {
        ArrayList<String> a=new ArrayList<>();
        a.add("我");
        a.add("是");
        a.add("笨");
        a.add("蛋");
        System.out.println(a);
        Iterator<String> it =a.iterator();
        String ele= it.next();
        System.out.println(ele);
        System.out.println(it.next());
        System.out.println(it.next());
        System.out.println(it.next());
    }
}

结果
[我, 是, 笨, 蛋]
我
是
笨
蛋

迭代器法2(重要)

public class test1 {
    public static void main(String[] args) {
        ArrayList<String> a=new ArrayList<>();
        a.add("我");
        a.add("是");
        a.add("笨");
        a.add("蛋");
        System.out.println(a);
        Iterator<String> it =a.iterator();

        while(it.hasNext()){
            String ele=it.next();
            System.out.println(ele);
        }
    }
}
结果
[我, 是, 笨, 蛋]
我
是
笨
蛋

增强for循环

foreach(增强for循环)遍历集合。
    foreach是一种遍历形式,可以遍历集合或者数组。
    foreach遍历集合实际上是迭代器遍历集合的简化写法。
    foreach遍历的关键是记住格式:
       for(被遍历集合或者数组中元素的类型 变量名称 : 被遍历集合或者数组){

       }
public class test2 {
    public static void main(String[] args) {
        Collection<String> a=new ArrayList<>();
        a.add("我");
        a.add("是");
        a.add("笨");
        a.add("蛋");
        System.out.println(a);

        for(String els :a){//这是集合
            System.out.println(els);
        }
        System.out.println("---------------------");
        double [] b={66,99,77};//遍历数组
        for (double ele : b) {
            System.out.println(ele);
        }
    }
}
结果是
[我, 是, 笨, 蛋]
我
是
笨
蛋
---------------------
66.0
99.0
77.0

案例

public class test {
    public static void main(String[] args) {
        Collection<movies> a=new ArrayList<>();
        a.add(new movies("好看的电影",9.0,"1"));
        a.add(new movies("不好看",0.0,"2"));
        a.add(new movies("一般的",5.5,"3"));
        System.out.println(a);
        for (movies s : a) {//不能用索引,所以只能用集合的强循环
            System.out.println("片名:  "+s.getNames());
            System.out.println("得分:  "+s.getScores());
            System.out.println("演员:  "+s.getActor());
        }
    }
}
public class movies {
    private String names;
    private double scores;
    private String actors;

    public movies(String names, double scores, String actor) {
        this.names = names;
        this.scores = scores;
        this.actors = actor;
    }

    public String getNames() {
        return names;
    }

    public void setNames(String names) {
        this.names = names;
    }

    public double getScores() {
        return scores;
    }

    public void setScores(double scores) {
        this.scores = scores;
    }

    public String getActor() {
        return actors;
    }

    public void setActor(String actor) {
        this.actors = actor;
    }
结果
片名:  好看的电影
得分:  9.0
演员:  1
片名:  不好看
得分:  0.0
演员:  2
片名:  一般的
得分:  5.5
演员:  3

常见数据结构

特点:后进先出,先进后出

队列

特点:先进先出,后进后出

数组

  • 查询速度快:查询数据通过地址值和索引定位,查询任意数据耗时相同
  • 删除效率低:要将原始数据删除,同时后面每个数据前移
  • 添加效率极低:添加位置后的每个数据后移,再添加元素

链表

  • 链表中的元素是在内存中不连续存储的,每个元素节点包含数据值和下一个元素的地址
  • 链表查询慢。无论查询哪个数据都要从头开始找
  • 链表增删相对快

二叉树

  • 只能有一个根节点,每个节点最多支持2个直接子节点
  • 节点的度: 节点拥有的子树的个数,二叉树的度不大于2 叶子节点 度为0的节点,也称之为终端结点。
  • 高度:叶子结点的高度为1,叶子结点的父节点高度为2,以此类推,根节点的高度最高。
  • 兄弟节点 :拥有共同父节点的节点互称为兄弟节点

普通二叉树

  • 每一个节点上最多有两个子节点
  • 左子树上所有节点的值都小于根节点的值
  • 右子树上所有节点的值都大于根节点的值
  • 目的:提高检数据的性能

红黑树

  • 红黑树是一种自平衡的二叉查找树,是计算机科学中用到的一种数据结构
  • 每一个节点可以是红或者黑;红黑树不是通过高度平衡的,它的平衡是通过“红黑规则”进行实现的。

红黑树规则

  • 每一个节点或是红色的,或者是黑色的,根节点必须是黑色
  • 如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)
  • 对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点

规则如下

每一个节点或是红色的,或者是黑色的,根节点必须是黑色

如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的

如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)

对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点

红黑树增删改查的性能都很好

List系列集合特点

ArrayList、LinekdList :有序,可重复,有索引

有序:存储和取出的元素顺序一致

有索引:可以通过索引操作元素

可重复:存储的元素可以重复

public class test {
    public static void main(String[] args) {

                // 1.创建一个ArrayList集合对象:
                // List:有序,可重复,有索引的。
               ArrayList<String> a = new ArrayList<>(); // 一行经典代码!
                a.add("Java");
                a.add("Java");
                a.add("HTML");
                a.add("HTML");
                a.add("MySQL");
                a.add("MySQL");
                // 在某个索引位置插入元素。
                a.add(2, "牛pi");
                System.out.println(a);
                //根据索引删除元素,返回被删除元素
                System.out.println(a.remove(1));
                System.out.println(a);
                // .根据索引获取元素:public E get(int index):返回集合中指定位置的元素。
                System.out.println(a.get(1));
                // 修改索引位置处的元素: public E set(int index, E element)
                System.out.println(a.set(1, "鸡"));
                System.out.println(a);
    }
}

 

List系列集合的遍历方式有:4种

第一种
for循环
for (int i = 0; i < lists.size(); i++) {
            String ele = lists.get(i);
            System.out.println(ele);
        }
第二种
迭代器
Iterator<String> it = lists.iterator();
        while (it.hasNext()){
            String ele = it.next();
            System.out.println(ele);
        }
第三种
foreach
System.out.println("-----------------------");
        for (String ele : lists) {
            System.out.println(ele);
        }
第四
Lambda表达式
 lists.forEach(s -> {
            System.out.println(s);
        });

ArrayList底层原理

ArrayList底层是基于数组实现的:根据索引定位元素快,增删需要做元素的移位操作

第一次创建集合并添加第一个元素的时候,在底层创建一个默认长度为10的数组

List<String> list = new ArrayList<>();
list.add("a");

LinkedList的特点

底层数据结构是双链表,查询慢,首尾操作的速度是极快的,所以多了很多首尾操作的特有API

 LinkedList也是List的实现类:底层是基于双链表的,增删比较快,查询慢!!

LinkedList是支持双链表,定位前后的元素是非常快的,增删首尾的元素也是最快的 所以LinkedList除了拥有List集合的全部功能还多了很多操作首尾元素的特殊功能:

  • public void addFirst(E e):将指定元素插入此列表的开头。
  • public void addLast(E e):将指定元素添加到此列表的结尾
  • public E getFirst():返回此列表的第一个元素。
  • public E getLast():返回此列表的最后一个元素。
  • public E removeFirst():移除并返回此列表的第一个元素。
  • public E removeLast():移除并返回此列表的最后一个元素。
  • public E pop():从此列表所表示的堆栈处弹出一个元素。
  • public void push(E e):将元素推入此列表所表示的堆栈。

import java.util.LinkedList;
public class test3 {
    public static void main(String[] args) {
        LinkedList<String > a=new LinkedList<>();//类比入栈
        a.addFirst("第一个子弹");
        a.addFirst("第二个");
        a.addFirst("第三个");
        a.addFirst("第四个");
        System.out.println(a);
        System.out.println(a.pop());
        System.out.println(a.pop());
        System.out.println(a.pop());
        System.out.println(a);//出栈  弹栈 pop
        LinkedList<String> q =new LinkedList<>();//入队出队
        q.addLast("1");
        q.addLast("2");
        q.addLast("3");
        System.out.println(q);
    }
}

集合的一些并发修改异常问题

案例 集合的删除问题

ArrayList<String> list = new ArrayList<>();
        list.add("1");
        list.add("2");
        list.add("2");
        list.add("4");
        list.add("5");
        list.add("6");
        System.out.println(list);
方法1 用迭代器
 Iterator<String> it = list.iterator();
while (it.hasNext()){
            String ele = it.next();
            if("2".equals(ele)){
               // 删除Java
                // list.remove(ele); // 集合删除会出毛病
                it.remove(); // 删除迭代器所在位置的元素值(没毛病)得用迭代器的
            }
        }
方法2 用for 循环
for (int i = list.size() - 1; i >= 0 ; i--) {
            String ele = list.get(i);
            if("2".equals(ele)){
                list.remove(ele);
            }
        }
        System.out.println(list);
    }
}
或者
for (int i =0; i < List.size() ; i++) {
            String ele = list.get(i);
            if("2".equals(ele)){
                list.remove(ele);
                i--;
            }
        }
        System.out.println(list);
    }
}

 

迭代器遍历集合但是用迭代器自己的删除方法操作可以解决。

使用for循环遍历并删除元素不会存在这个问题。

目标:泛型的概述。

什么是泛型?

泛型就是一个标签:<数据类型>
泛型可以在编译阶段约束只能操作某种数据类型。
泛型可以在编译阶段约束只能操作某种数据类型。
泛型只能支持引用数据类型。

泛型类

import java.util.ArrayList;

public class MyArrayList<E> {         //泛型类
    private ArrayList lists = new ArrayList();

    public void add(E e){
        lists.add(e);
    }

    public void remove(E e){
        lists.remove(e);
    }

    @Override
    public String toString() {
        return lists.toString();
    }
}
public class Test {
    public static void main(String[] args) {
        // 需求:模拟ArrayList定义一个MyArrayList ,关注泛型设计
        MyArrayList<String> list = new MyArrayList<>();
        list.add("Java");
        list.add("Java");
        list.add("MySQL");
        list.remove("MySQL");
        System.out.println(list);

        MyArrayList<Integer> list2 = new MyArrayList<>();
        list2.add(23);
        list2.add(24);
        list2.add(25);
        list2.remove(25);
        System.out.println(list2);
    }
}

泛型方法

定义方法时同时定义了泛型的方法就是泛型方法

泛型方法的格式:修饰符 <泛型变量> 方法返回值 方法名称(形参列表){}

注意:方法定义了是什么泛型变量,后面就只能用什么泛型变量。
 public <T> void show(T t) {  }

作用:方法中可以使用泛型接收一切实际类型的参数,方法更具备通用性

public static <T> T[] getArr(T[] arr){
        return arr;
    }

    public static <T> void printArray(T[] arr){
        if(arr != null){
            StringBuilder sb = new StringBuilder("[");
            for (int i = 0; i < arr.length; i++) {
                sb.append(arr[i]).append(i == arr.length - 1 ? "" : ", ");
            }
            sb.append("]");
            System.out.println(sb);
        }else {
            System.out.println(arr);
        }
    }
}

泛型方法核心思想

把出现泛型的地方全部转成真实数据类型

泛型接口的概述

使用了泛型定义的接口就是泛型接口。 泛型接口的格式:修饰符 interface 接口名称<泛型变量>{}

泛型的通配符

? 可以在“使用泛型”的时候代表一切类型

 E T K V 是在定义泛型的时候使用的

泛型的上下限

 ? extends Car: ?必须是Car或者其子类   泛型上限

? super Car : ?必须是Car或者其父类   泛型下限

运用案例

public class GenericDemo {
    public static void main(String[] args) {
        ArrayList<BMW> bmws = new ArrayList<>();
        bms.add(new BM());
        bms.add(new BM());
        bms.add(new BM());
        go(bmws);

        ArrayList<BENZ> benzs = new ArrayList<>();
        bzs.add(new BZ());
        bzs.add(new BZ());
        bzs.add(new BZ());
        go(benzs);

        ArrayList<Dog> dogs = new ArrayList<>();
        dogs.add(new Dog());
        dogs.add(new Dog());
        dogs.add(new Dog());
        // go(dogs);
    }

    /**
       所有车比赛
     */
    public static void go(ArrayList<? extends Car> cars){ //进行上下限的限制,防止
    }
}

class Dog{

}

class BZ extends Car{
}

class BM extends Car{
}

// 父类
class Car{
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值