几个重要知识点补充

LinkedList

如何体现双向链表?

void addFirst() 将指定元素插入开头

void addLast() 将指定元素添加到结尾

E getFirst() 返回列表的第一个元素

E getLast() 返回列表的最后一个元素

boolean offer(E e) 将元素添加到列表的末尾(最后一个元素)

boolean offerFirst(E e) 将元素添加到列表的开头

boolean offerLast(E e) 将元素添加到列表的结尾

int indexOf(Object o) 返回列表中首次出现该元素的索引,没有返回-1

int lastIndexOf(Object o) 返回列表中最后出现该元素的索引,没有返回-1

E get(int index) 返回指定位置的元素

如何体现栈?

void push(E e) 将元素推入列表所表示的堆栈

E peek() 获取但不移除(第一个元素)

E peekFirst() 获取但不移除第一个元素

E peekLast() 获取但不移除最后一个元素

E pop() 弹出一个元素

如何体现队列?实现Queue接口

抛出异常 返回特殊值
插入 add(e) offer(e)
移除 remove() poll()
检查 element() peek()

如何体现双端队列?实现Deque接口

抛出异常 返回特殊值
插入第一个 addFirst(e) offerFirst(e)
插入最后 addLast(e) offerLast(e)
移除第一个 removeFirst() pollFirst()
移除最后一个 removeLast() pollLast()
检查第一个 getFirst() peekFirst()
检查最后一个 getLast() peekLast()

不需要线程安全时:Deque deque = new LinkedList<>();

优先队列PriorityQueue

Queue接口的实现,可以对其中元素进行排序,默认是升序排列

常用方法

peek();	//返回队首元素
poll();	//返回队首元素,队首元素出队列
add();	//添加元素
size();	//返回元素个数
isEmpty();	//判断队列是否为空

Comparator接口的Lambda表达式

o1-o2是升序,o2-o1是降序

//1.方法1:
Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
//intervals数组按升序排列
// 2. 方法2:
Arrays.sort(intervals, new Comparator<int[]>() {
    @Override
    public int compare(int o1[], int o2[]) {
        return o1[0] - o2[0];
    }
});

//二维数组降序排列:
Arrays.sort(people, (o1, o2) -> o1[0] == o2[0] ? o1[1] - o2[1]: o2[0] - o1[0]);

位运算

位操作符

  • &与运算
    • 两个都为1——1
  • |或运算
    • 有一个为1——1
  • ^异或运算
    • 两位不同——1
  • ~取反运算
    • 是0——1
  • 左移运算
    • 高位丢弃,低位补0
  • 右移运算
    • 无符号数,高位补0
    • 有符号数,高位补符号数

常见位运算问题

  • a向右移一位——a/2,向下整除

  • a向左移一位——a*2

  • 异或操作交换两数

    void swap(int a, int b){
      a ^= b;
      b ^= a;
      a ^= b;
    }
    
  • 判断奇偶数

    和1与操作,为1就是奇数,为0就是偶数

    if(0 == (a&1)){
      //偶数
    }
    //所以也可以用来求n%2的余数
    
  • 交换符号

    int reversal(int a){
      return ~a+1;
    }
    
  • 求绝对值

    先判断符号:整数右移31位是0,负数右移31位是-1

    整数绝对值是本身,负数绝对值是交换符号的本身

    int abs(int a){
      int i = a>>31;
      return i == 0? a : (~a+1);
    }
    

Java各类型转换

StringBuilder ---> String

String str = "awswondeoifoivnfr";
StringBuilder sb = new StringBuilder(str);

整型数组 ----> 字符串

StringBuilder sb = new StringBuilder();
for(int i=i; i<n; i++){
  s.append(String.valueOf(a[i]));
}
String str = "" + sb;

字符串 ----> 整型数组

String str = "123456";
int[] a = new int[str.length()];
for(int i=0; i<str.length(); i++){
  a[i] = str.charAt(i) - '0';
}

字符串 ----> 字符数组

char[] c = str.toCharArray();

字符数组 ----> 字符串

String str = new String(c);

字符数组 ----> 整型数组

char[] c = {'1', '2', '3', '4'};
int[] a = new int[c.length];
for(int i=0; i<c.length; i++){
  a[i] = c[i] - '0';
}

整型数组 ----> 字符数组

int[] a = {1,2,3,4,5};
char[] c = new char[a.length];
for(int i=0; i<a.length; i++){
  c[i] = (char)(a[i] + '0');
}

整型数 ----> 字符串

//1
String str = Integer.toString(i);
//2
String str = String.valueOf(i);
//3
String str = "" + i;
//Double, Float, Long 同理

字符串 ----> 整型数

//1
int i = Integer.parseInt(String);
//2
int i = Integer.valueOf(str).intValue();
//Double, Float, Long 同理

数据类型

一个字节是8位

  • int
    • byte(8 bits) short(16 bits) int(32 bits) long(64 bits)
  • float
    • 单精度(32bits float) 双精度(64bits double)
  • char
    • 16bits
  • boolean
    • 8 bits
转换原则

从低到高

byte -> short -> int -> long -> float -> double -> char

List ----> Array

List<String> list = new ArrayList<>();
Object[] array = list.toArray(new String[List.size()]);//直接指定类型。防止强制转换时抛异常

Array ----> List

List<String> list = new ArrayList<String>(Arrays.asList(array));

KMP算法

有一个文本串S,和一个模式串P,现在要查找P在S中的位置,怎么查找呢?

暴力匹配

假设现在文本串S匹配到 i 位置,模式串P匹配到 j 位置,则有:

  • 如果当前字符匹配成功(即S[i] == P[j]),则i++,j++,继续匹配下一个字符;
  • 如果失配(即S[i]! = P[j]),令i = i - (j - 1),j = 0。相当于每次匹配失败时,i 回溯,j 被置为0。

KMP算法(Knuth-Morris-Pratt三个人)

假设现在文本串S匹配到 i 位置,模式串P匹配到 j 位置

  • 如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++,继续匹配下一个字符;
  • 如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]。此举意味着失配时,模式串P相对于文本串S向右移动了j - next [j] 位。

next数组

next[i]表示的是前i的字符组成的这个子串最长的相同前缀后缀的长度

对于每模式串 t 的每个元素 t j,都存在一个实数 k ,使得模式串 t 开头的 k 个字符(t 0 t 1…t k-1)依次与 t j 前面的 k(t j-k t j-k+1…t j-1,这里第一个字符 t j-k 最多从 t 1 开始,所以 k < j)个字符相同。如果这样的 k 有多个,则取最大的一个。

void Getnext(int next[],String t)
{
   int j=0,k=-1;
   next[0]=-1;
   while(j<t.length-1)
   {
      if(k == -1 || t[j] == t[k])
      {
         j++;k++;
         if(t[j]==t[k])//当两个字符相同时,就跳过
            next[j] = next[k];
         else
            next[j] = k;
      }
      else k = next[k];
   }
}

KMP的实现

int KMP(String s,String t)
{
   int next[MaxSize],i=0;j=0;
   Getnext(t,next);
   while(i<s.length&&j<t.length)
   {
      if(j==-1 || s[i]==t[j])
      {
         i++;
         j++;
      }
      else j=next[j];               //j回退。。。
   }
   if(j>=t.length)
       return (i-t.length);         //匹配成功,返回子串的位置
   else
      return (-1);                  //没找到
}

Iterator迭代器

迭代器可以遍历并选择序列中的对象,被称为“轻量级”对象

功能简单,并且只能单向移动

  1. 第一次调用next()方法时,返回序列的第一个元素
  2. next()获得序列的下一个元素
  3. hasNext()检查序列中是否还有元素
  4. remove()将迭代器新返回的元素删除
public static void main(String[] args){
  List<String> list = new ArrayList<String>();
  list.add("aaa");
  list.add("bbb");
  list.add("ccc");
  
  Iterator<String> it = list.iterator();
  while(it.hasNext()){
    String a = it.next();
    System.out.println(a);
    if("bbb".equals(a)){
      it.remove();
    }
  }
}

功能更强大的ListIterator

  1. 可以向前、向后两个方向遍历List
  2. 在遍历时可以修改List的元素
  3. 遍历时获取迭代器当前游标所在位置(迭代器没有当前元素,只有游标一个概念,在元素之间)
新增方法
  • void hasPrevious() 判断游标前面是否有元素
  • Object previous() 返回游标前面的元素,同时游标前移
  • int nextIndex() 返回游标后边元素的索引位置,初始为0
  • int previousIndex() 初始为-1,同时报错
  • void add(E) 在游标前面插入一个元素
  • void set(E) 更新迭代器最后一次操作的元素为E,即上一个调用next()或previous()返回的元素
  • void remove() 删除迭代器最后一次操作的元素

要实现由后向前的遍历,应先实现由前向后的遍历

单例模式

单例最重要的思想:构造器私有,别人就无法new这个对象了

饿汉式单例

public class Hungry{
  private Hungry(){
    
  }
  //饿汉式·一上来就把这个对象加载了:
  private final static Hungry HUNGRY = new Hungry();//先new一个hungry出来,这个是唯一的
  //可能会浪费空间
  public static Hungry getInstance(){
    return HUNGRY;
  }
}

懒汉式单例

public class LazyMan{
  private LazyMan(){ 
    //为防止反射破坏
    synchronized(LazyMan.class){
      if(lazyMan!=null){
        throw new RuntimeException("不要试图使用反射!");
      }
    }
  }
  private volatile static LazyMan lazyMan;//避免new LazyMan()指令重排
  
  public static LazyMan getInstance(){
    if(lazyMan == null){
      lazyMan = new LazyMan();//不是原子性操作
    }
    return lazyMan;
  }
  //单线程没问题
  //多线程并发需要加锁
  public static LazyMan getInstance(){
    //双层检测锁模式的懒汉式单例	DCL懒汉式
    if(lazyMan==null){
      synchronized(LazyMan.class){
        if(lazyMan==null){
          lazyMan = new LazyMan();
        }
      }
    }
    return lazyMan;
  }
  //反射可以破坏单例
  public static void main(String[] args){
    LazyMan instance = LazyMan.getInstance();
    Constructor<LazyMan> deCon = LazyMan.class.getDeclaredConstructor(null);//get空参构造器
    deCon.setAccessible(true);//无视了私有构造器
    LazyMan instance2 = deCon.newInstance();//通过反射创建对象
    //instance和instance2已经不是同一个了,违背了单例
  }
}

静态内部类

public class Holder{
  private Holder(){
  }
  public static Holder getInstance(){
    return InnerClass.HOLDER;
  }
  public static class InnerClass{
    private static final Holder HOLDER = new holder();
  }
}

单例不安全,因为有反射

使用枚举

枚举

public enum Direction{
  FRONT, BEHIND, LEFT, RIGHT;//都是本类的实例,一共有四个实例对象
}
Direction d = Direction.FRONT;//类名.枚举项,不能使用new创建对象

枚举和switch

enum Signal{
  RED, YELLOW, GREEN;
}

public static String getTrafficInstruct(Signal signal){
  String instruct = "信号灯故障";
  switch(signal){
    case RED:
      instruct = "红灯停";
      break;
    case YELLOW:
      instruct = "黄灯";
      break;
    case GREEN:
      instruct = "绿灯行";
      break;
    default:
      break;
  }
  return instruct;
}
posted @ 2021-02-24 12:06  GladysChloe  阅读(63)  评论(0)    收藏  举报