你真的了解集合吗,来给我说一下集合的底层数据结构!(下)

简介: 数据结构就是计算机存储、组织数据的方式。 在计算机科学中,算法的时间复杂度是一个函数,它定性描述了该算法的运行时间,常用O符号来表述。 时间复杂度是同一问题可用不同算法解决,而一个算法的质量优劣将影响到算法乃至程序的效率。算法分析的目的在于选择合适算法和改进算法

九、Map接口


Map翻译为映射

36.JPG

从结构图上看,Map并不是集合,而是类似两个集合的映射关系,所以Map中没有实现Collection接口


在Map中,要求A集合的每一个元素(key)都可以在B集合中找到唯一的值(value)与之对应,意味着A集合中的元素是不可以重复的而B集合中的元素却可以重复,所以A集合应该是一个Set集合,而B集合是一个List集合

37.JPG

其实一个Map中就由很多的key-value(kv键值对)组成的,每一个键值对我们用Entry表示

38.JPG

其实我们也可以把Map看成是多个Entry的集合


39.JPG

总的来说,我们还是习惯把Map称为集合,不过和List、Set不同,List、Set是单元素集合,Map是双元素集合


  • 单元素集合:每一次只能存储一个元素,比如List、Set
  • 双元素集合:每次需要存储两个元素(一个key一个value),比如Map注意:
  1. Map接口并没有继承Collection接口也没有继承Iterable(可迭代的)接口,所以不可以直接对Map使用for-each遍历操作
  2. 其中value就表示存储的数据,而key就是这个value的名字


9.1、Map常用的API


9.1.1、添加操作


  1. boolean put(Object key,Object value):存储一个键值对到Map中
  2. boolean putAll(Map m):把m中的所有键值对添加到当前Map中


9.1.2、删除操作


Object remove(Object key):从Map中删除指定key的键值对,并返回被删除key对应的value


9.1.3、修改操作


无专门的方法,可以调用put方法,存储相同key,不同value的键值对,可以覆盖原来的。


9.1.4、查询操作


  1. int size():返回当前Map中键值对个数
  2. boolean isEmpty():判断当前Map中键值对个数是否为0.
  3. Object get(Object key):返回Map中指定key对应的value值,如果不存在该key,返回null
  4. boolean containsKey(Object key):判断Map中是否包含指定key
  5. boolean containsValue(Object value):判断Map中是否包含指定value
  6. Set keySet():返回Map中所有key所组成的Set集合
  7. Collection values():返回Map中所有value所组成的Collection集合
  8. Set entrySet():返回Map中所有键值对所组成的Set集合


9.1.5、Lambda 8表达式集合遍历


map.forEach((k,v)->{
   System.out.println(k+" "+v); 
});
复制代码


9.2、HashMap


HashMap底层是基于哈希表算法,Map中存储的key对象和HashCode值决定了在哈希表中存储的位置,因为Map中的key是Set,所以不能保证添加的先后顺序,也不允许重复


import java.util.HashMap;
import java.util.Map;
public class HashMapDemo1{
  public static void main(String[] args) {
    Map<String, String> map = new HashMap<>();
    map.put("girl1", "西施");
    map.put("girl2", "王昭君");
    map.put("girl3", "貂蝉");
    map.put("girl4", "杨玉环");
    System.out.println("map中有多少键值对:"+map.size());
    System.out.println(map);
    System.out.println("是否包含key为girl1:"+map.containsKey("girl1"));
    System.out.println("是否包含value为貂蝉:"+map.containsValue("貂蝉"));
//替换key为girl3的value值
    map.put("girl3", "小乔");
    System.out.println(map);
//删除key为girl3的键值对
    map.remove("girl3");
      System.out.println(map);
  }
}
复制代码


9.2.1、Map的迭代遍历


//获取Map中所有的key
Set<String> keys = map.keySet();
System.out.println("Map中所有key:"+keys);
//获取Map中所有的value
Collection<String> values = map.values();
System.out.println("Map中所有value:"+values);
//获取Map中所有的key-value(键值对)
Set<Entry<String, String>> entrys = map.entrySet();
for (Entry<String, String> entry : entrys) {
  String key = entry.getKey();
  String value = entry.getValue();
  System.out.println(key+"->"+value);
}
复制代码


9.2.2、练习


统计一个字符串中每隔字符出现次数


package day14_ArrayList2.classing;
import java.util.HashMap;
import java.util.Scanner;
/**
 * @author Xiao_Lin
 * @version 1.0
 * @date 2020/12/17 16:54
 */
public class Test1 {
  //需求:统计一个字符串中每一个字符出现的次数
  public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    String str = scanner.nextLine();
    HashMap<Character, Integer> map = new HashMap();//新建一个map,map的key存储字符,value存储出现的次数
    //进行map的遍历
      for (int i = 0 ;i<str.length();i++){
        char c = str.charAt(i);//将str字符串遍历并且强转为字符
        if (map.containsKey(c)){//map是否包含遍历的字符
          Integer value = map.get(c);//如果包含就将原来的字符对应的value(次数)值取出来并且自增
          value+=1;//次数自增
          map.put(c,value);//将修改后的value放入map集合中
        }else {
          map.put(c,1);//否则将字符放进集合中,并且将次数置为1
        }
      }
    System.out.println(map);
  }
}
复制代码


9.3、TreeMap


TreeMap底层的key是基于红黑树,因为Map中的key也是一个Set集合,所以不能保证添加的先后顺序,且也不允许重复,因为Map中存储的key会默认使用自然排序(从小到大),和TreeSet一样,除了可以使用自然排序也可以自定义自己的排序


import java.util.Map;
import java.util.TreeMap;
public class App {
  public static void main(String[] args) {
    Map<String, String> map = new TreeMap<>();
    map.put("girl4", "杨玉环");
    map.put("girl2", "王昭君");
    map.put("key1", "西施");
    map.put("key3", "貂蝉");
    System.out.println(map);
//-------------------------------------------
    map = new TreeMap<>(map);
    System.out.println(map);
  }
}
复制代码
package day14_ArrayList2.classing;
import java.util.Comparator;
import java.util.TreeSet;
/**
 * @author Xiao_Lin
 * @version 1.0
 * @date 2020/12/17 15:55
 */
public class TreeSet1 {
  public static void main(String[] args) {
    //使用自定义的比较器,需要传入一个Comparator接口的实现类,这里使用匿名内部类
    TreeSet<String> set = new TreeSet<String>(new Comparator<String>() {
      @Override
      public int compare(String s1, String s2) {
       if (s1.length()==s2.length()){
         return s1.compareTo(s2);
       }else {
         return s1.length()-s2.length();
       }
      }
    });
    set.add("s12");
    set.add("see");
    set.add("s1234");
    System.out.println(set);
  }
}
复制代码


十、异常


异常是指在程序的运行过程中所发生的不正常现象,它会中断正在运行的程序,异常不是错误,更不是逻辑的错误


异常会导致程序中断进行


40.JPG

Java异常处理机制


Java编程语言使用异常处理机制为程序提供了异常处理的能力,异常处理机制可以保证程序出现异常后,继续向正确的方向运行 。


41.JPG

异常处理的分类


异常处理包含两种代码块:

  1. try...catch
  2. try...catch...finally


10.1、异常对象


异常对象是出现异常时的那条语句自动产生的一个对象,由JVM自动创建,异常在Java类中通过Exception或者其他具体的子类创建,命名规则是:异常类型+Exception,Exception是所有异常的父类,他有如下方法


方法 作用
toString() 返回异常类型和异常信息
getMessage() 返回异常信息
printStackTrace 打印堆栈信息(红色)

堆栈的信息大致如下:


42.JPG

第一句:由异常类型和异常的Message构成

最后一句:异常发生的具体位置


10.2、try...catch


把可能产生异常的代码放到try中,如果代码产生了异常由catch捕获异常,然后交由catch里面的代码请求处理


10.2.1、语法格式


try{
    //可能产生异常的代码块
}catch(异常类型 e){//异常由catch捕获
    //异常处理
}
复制代码


10.2.2、try...catch执行情况分析


10.2.2.1、无异常


如果没有发现异常,则程序正常执行


10.2.2.2 、异常被catch捕获


程序出现了异常,且异常被catch捕获,也就是说异常的类型符合catch中的类型,此时进行异常处理,处理完成后程序正常执行


package day15_exception.classing.exception;
import java.util.Scanner;
/**
 * @author Xiao_Lin
 * @version 1.0
 * @date 2020/12/19 11:31
 */
public class TestException {
  public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    try {
      System.out.println("请输入被除数:");
      int num1 = scanner.nextInt();
      System.out.println("请输入除数:");
      int num2 = scanner.nextInt();
      System.out.println(num1/num2);
    }catch (Exception e){ 
      System.out.println("出现异常");
      System.out.println(e.getMessage());
    }
  }
}
复制代码


43.JPG


总结

异常发生后,从异常发生的那句代码开始,程序不继续向下运行,立即转入异常处理


10.2.2.3、异常不匹配


异常不匹配时,程序中断


package day15_exception.classing.exception;
import java.util.Scanner;
/**
 * @author Xiao_Lin
 * @version 1.0
 * @date 2020/12/19 11:31
 */
public class TestException {
  public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    try {
      System.out.println("请输入被除数:");
      int num1 = scanner.nextInt();
      System.out.println("请输入除数:");
      int num2 = scanner.nextInt();
      System.out.println(num1/num2);
    }catch (ArithmeticException e){//这里我们捕获的是除数异常,但是我们模拟的是InputMismatchException(输入匹配异常),所以程序2到这里并没有匹配到合适的异常中断程序
      System.out.println("出现异常");
      e.printStackTrace();
    }
  }
}
复制代码


44.JPG


10.2.3、多重catch


我们可以为 try 代码书写多个 catch 用于捕获多个具体的异常,但是要注意在书写的时候,子类在上,父类在下,因为Java的代码从上往下执行,没有合适的异常就用最大的异常来进行捕获。


package day15_exception.classing.exception;
import java.util.InputMismatchException;
import java.util.Scanner;
/**
 * @author Xiao_Lin
 * @version 1.0
 * @date 2020/12/19 11:31
 */
public class TestException {
  public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    try {
      int num1 = scanner.nextInt();
      System.out.println("请输入除数:");
      int num2 = scanner.nextInt();
      int r = num1 / num2;
      System.out.println(r);
    }catch (InputMismatchException e) {
      System.out.println("输入数据有误");
    }catch(ArithmeticException e) {
      System.out.println("除数不能为 0");
    }catch(Exception e) {
      System.out.println("未知异常");
    }
    System.out.println("程序正常结束!");
  }
}
复制代码


10.3、try...catch...finally


try...catch 和之前一样用于捕获并处理异常,finally 代码块用于处理异常后的收尾工作。


不管是否发生异常,finally 总是执行,但是finally的工作仅仅只是包含释放内存、关闭文件、关闭网络连接、关闭数据库连接等收尾工作。


在finally中不建议书写代码和修改变量的值,因为finally无法修改临时堆栈的值


package day15_exception.classing.exception;
import java.util.InputMismatchException;
import java.util.Scanner;
/**
 * @author Xiao_Lin
 * @version 1.0
 * @date 2020/12/19 11:31
 */
public class TestException {
  public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    try {
      System.out.println("请输入被除数");
      int num1 = scanner.nextInt();
      System.out.println("请输入除数:");
      int num2 = scanner.nextInt();
      int r = num1 / num2;
      System.out.println(r);
    }catch (InputMismatchException e) {
      System.out.println("输入数据有误");
    }catch(ArithmeticException e) {
      System.out.println("除数不能为 0");
    }catch(Exception e) {
      System.out.println("未知异常");
    }finally {
      System.out.println("我是finally");
    }
    System.out.println("程序正常结束!");
  }
}
复制代码

finally唯一不执行的情况是:JVM正常退出

package day15_exception.classing.exception;
import java.util.InputMismatchException;
import java.util.Scanner;
/**
 * @author Xiao_Lin
 * @version 1.0
 * @date 2020/12/19 11:31
 */
public class TestException {
  public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    try {
      System.out.println("请输入被除数");
      int num1 = scanner.nextInt();
      System.out.println("请输入除数:");
      int num2 = scanner.nextInt();
      int r = num1 / num2;
      System.out.println(r);
    } catch (Exception e) {
      System.out.println(e.toString());
    // jvm 正常退出
      System.exit(0);
    } finally {
      System.out.println("我是 finally");
    }
    System.out.println("程序正常结束!");
  }
}
复制代码


10.3.1、存在 return 的 try...catch...finally


package day15_exception.classing.exception;
import java.util.InputMismatchException;
import java.util.Scanner;
/**
 * @author Xiao_Lin
 * @version 1.0
 * @date 2020/12/19 11:31
 */
public class TestException {
  public static int div(int a, int b) {
    int r = 0;
    try {
      r = a / b;
      return r;
    } catch (Exception e) {
      System.out.println(e.toString());
    } finally {
      System.out.println("我是 finally");
    }
    return r;
  }
  public static void main(String[] args) {
    System.out.println(div(10, 0));
    System.out.println("程序正常结束!");
  }
}
复制代码


结论

  1. 存在 return 的 try...catch...finally中,funally先执行,然后再return
  2. return总是最后执行的


快捷键

快速打出try...catch的快捷键

  • Eclipse : Shift+Alt+Z
  • IDEA : Ctrl+Alt+T


10.4、异常分类


异常的继承体系,Throwable 类是 Java 语言中所有错误(Error)或异常的父类。


45.JPG


10.5、Error


Error,表示代码运行时 JVM(Java 虚拟机)出现的问题。如系统崩溃或内存溢出等,不需要处理 Error,我们唯一能做的就是等待,在开发中极少见到,常见的有以下几个


Error类型 描述
StackOverflowError 当应用程序递归太深而发生堆栈溢出时,抛出该错误。比如死循环或者没有出口的递归调用
OutOfMemoryError 因为内存溢出或没有可用的内存提供给垃圾回收器时,Java 虚拟机无法分配一个对象,这时抛出该错误。比如 new 了非常庞大数量的对象而没释放。


10.6、Exception


Exception表示程序在运行时出现的一些不正常情况,一般大多数表示轻度到中度的问题,属于可预测、可恢复问题。如除数为 0,数组索引越界等,这种情况下,程序员通过合理的异常处理,确保程序的正常运行直到结束。


异常的构造方法

//构造一个异常信息为null的新异常
Exception();
//构造一个异常信息为 message的新异常
Exception(String message)
复制代码


异常的分类

  1. 运行时异常
  2. 检查时异常


10.6.1、运行时异常


RuntimeException(运行时异常)是指在程序运行过程中出现的异常时可处理、可不处理的异常,他的父类是RuntimeException


运行时异常 描述
InputMismatchException 输入不匹配异常
ArithmeticException 数学计算异常(比如除数为0)
IndexOutOfBoundsException 下标/索引越界异常
ArrayIndexOutOfBoundsException 数组下标越界异常
StringIndexOutOfBoundsException 字符串下标越界
NullPointerException 空指针异常
IllegalArgumentException 方法接收到非法参数
ClassCastException 强制类型转换异常
NumberFormatException 数字格式转换异常,如把"abc"转换成数字


10.6.2、检查时异常


检查时异常(Checked Exception):也称编译时异常,指在编译期间检查程序可能存在不正常的情况,在程序运行过程中必须处理,否则编译不通过。在 Java 中没有特定的父类,一般用 Exception 表示检查时异常。


检查时异常 描述
ParseException 格式(日期时间等)解析异常
ClassNotFoundException class没找到异常
FileNotFoundException 文件未找到异常
IOException IO异常
SQLException SQL异常
UnsupportedEncodingException 不支持字符串编码异常


10.7、声明异常


10.7.1、throws


我们可以通过throws来声明某个方法可能出现的异常。

当方法的定义者在定义方法的时候不知道调用者在调用该方法的时候会出现异常,但是定义者又不知道如何处理时,此时可以选择使用throws关键字来声明异常,可以声明多个异常,用逗号分隔


[修饰符] 返回值类型 方法名(形参列表) throws 异常 1, 异常 2, 、、、{
}
复制代码

声明异常时要根据异常的分类来确定是否外界(调用处)是否必须处理该方法产生的异常。如果需要外界必须处理,需要声明检查时异常,否则声明运行时异常。


package day15_exception.classing.exception;
import java.util.InputMismatchException;
import java.util.Scanner;
/**
 * @author Xiao_Lin
 * @version 1.0
 * @date 2020/12/19 11:31
 */
public class TestException {
  public static int div(int a, int b) throws Exception{//抛出了检查时异常
    int r = 0;
      r = a / b;
      return r;
  }
  public static void main(String[] args) {
    System.out.println(div(10, 0));//这里并没有解决异常,会报错,编译无法通过
    System.out.println("程序正常结束!");
  }
}
复制代码
public static void main(String[] args) {//改为这样才可以
    try {
      System.out.println(div(10, 0));
    } catch (Exception e) {
      e.printStackTrace();
    }
    System.out.println("程序正常结束!");
  }
复制代码


10.7.2、声明异常与重载的关系


无关系


10.7.3、声明异常和重写的关系


如果父类声明运行时异常,子类可以声明运行时异常或者不声明;如果父类声明检查时异常,子类可以声明检查时异常或者不声明或者运行时异常。


如果父类没有声明任何异常,子类要么不声明任意异常,要么可以声明运行时异常,但不能声明检查时异常。


总结:子类方法的异常 <= 父类方法的异常


10.7.4、异常上抛


在实际开发过程中,如果调用 A 类 方法存在异常,调用者也不知如何处理这个异常时,可以继续向上声明异常,我们把这个过程称为异常的上抛。


在实际中遇到的异常能内部处理的优先处理,无法处理再上抛

public class Sub extends Parent{
  @Override
  public void showInfo() throws Exception{
  }
}
复制代码
public class Test01 {
  public static void main(String[] args) throws Exception{
    Sub sub = new Sub();
    sub.showInfo();
    System.out.println("程序正常结束!");
  }
}
复制代码


10.7.5、手动抛出异常


在实际开发过程中,开发者也可以根据程序的需要,手动抛出异常,通过 throw 关键字,程序员可以主动抛出某种特定类型的异常。


package com.kal02.exception07;
public class Student {
  private String name;
  private String gender;
public void setGender(String gender) throws RuntimeException{
  if(gender.equals(Student.MAN)||gender.equals(Student.WOMAN)){
    this.gender = gender;
  }else {
    // 手动抛出异常
    throw new RuntimeException("性别只能是男或者女");
    }
  }
}
复制代码


10.8、自定义异常


当 JDK 中的异常类型不能满足程序的需要时(也即需要定义具体的业务异常时),可以自定义异常。


自定义异常的步骤:


  1. 确定异常类型(检查时异常、运行时异常)。
  2. 继承异常的父类(检查时异常继承Exception,运行时异常继承RuntimeException)
  3. 声明构造方法(自定义异常类的构造方法,并且将异常信息传进去,父类的构造方法子类无法继承)


Student类

package day15_exception.classing.customize;
/**
 * @author Xiao_Lin
 * @date 2020/12/19 17:25
 */
public class Student {
  private String name;
  private String Gender;
  public Student(String name, String gender) {
    this.name = name;
    Gender = gender;
  }
  public Student() {
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public String getGender() {
    return Gender;
  }
  public void setGender(String gender) throws GenderException{
    if (gender.equals("男") || gender.equals("女") || gender.equals("保密")){
      Gender = gender;
    }else {
      throw new GenderException("性别不合法");//s
    }
  }
}
复制代码


自定义异常类

package day15_exception.classing.customize;
/**
 * @author Xiao_Lin
 * @date 2020/12/19 17:27
 */
public class GenderException extends Exception{
  public GenderException() {
  }
  public GenderException(String message) {
    super(message);
  }
}
复制代码


测试类

package day15_exception.classing.customize;
/**
 * @author Xiao_Lin
 * @date 2020/12/19 17:28
 */
public class CustomizeException {
  public static void main(String[] args) {
    Student student = new Student("宿舍","钕");
    try {
      student.setGender("宿舍");
    } catch (GenderException e) {
      e.printStackTrace();
    }
  }
}
复制代码


46.JPG


10.9、面试题


Java异常中throw和throws的区别


  • throws:
  1. 用来声明一个方法可能产生的所有异常,不做任何处理而是将异常往上传,谁调用我我就传给谁(甩锅)。
  2. 用在方法声明后面跟的是异常类名。
  3. 可以声明多个异常,用,隔开。
  4. throws表示的是一种可能性,仅仅只是说可能会出现这种异常,并不是百分之百会出现这些异常。


  • throw:
  1. throw表示用来抛出某一个具体的异常。
  2. 用在方法体内,跟的是某一个具体的异常的对象名。
  3. 只能抛出一个异常对象名。
  4. 表示抛出了异常,由方法体内的语句处理。
  5. throw表示出现了异常,则throws表示一定出现了某种异常。



相关文章
|
1天前
|
算法 Java 数据库连接
Spring+MySQL+数据结构+集合,Alibaba珍藏版mybatis手写文档
Spring+MySQL+数据结构+集合,Alibaba珍藏版mybatis手写文档
|
3天前
|
存储 程序员 索引
数据结构深度剖析:列表、元组、字典和集合
【4月更文挑战第8天】Python的四种基础数据结构——列表、元组、字典和集合,各自拥有独特的特性和应用场景。列表是可变序列,方便增删改元素;元组不可变,常用于保证数据不变性;字典是键值对容器,快速访问通过键;集合是无序不重复元素集,适合成员测试和去重。理解并灵活运用这些数据结构,能提升代码效率,有效处理和分析数据。
|
3天前
|
索引 Python
Python数据结构讲解集合
Python数据结构讲解集合
18 0
|
3天前
|
缓存 算法 安全
Java集合框架:深入探究数据结构与算法的精华
Java集合框架:深入探究数据结构与算法的精华
|
3天前
|
存储 算法 Java
【数据结构与算法】2、链表(简单模拟 Java 中的 LinkedList 集合,反转链表面试题)
【数据结构与算法】2、链表(简单模拟 Java 中的 LinkedList 集合,反转链表面试题)
43 0
|
3天前
|
存储 消息中间件 人工智能
数据结构与集合源码
数据结构与集合源码
28 0
|
3天前
|
存储 前端开发 Java
【Java】集合与数据结构
【Java】JavaSE集合
87 0
|
3天前
|
Serverless 数据库 索引
Python基础语法、内建数据结构列表、元组、字典、集合的讲解及应用(附源码 超详细必看)
Python基础语法、内建数据结构列表、元组、字典、集合的讲解及应用(附源码 超详细必看)
55 0