1-7

1. java.lang.String
(1) replace()和replaceAll()
废话不多说直接上源代码
replace()
/**
     * Replaces each substring of this string that matches the literal target
     * sequence with the specified literal replacement sequence. The
     * replacement proceeds from the beginning of the string to the end, for
     * example, replacing "aa" with "b" in the string "aaa" will result in
     * "ba" rather than "ab".
     *      翻译:
     *      1.将字符串里匹配目标串的子串替换为指定的代替序列
     *      2.替换过程从头到尾进行
     * @param  target The sequence of char values to be replaced
     * @param  replacement The replacement sequence of char values
     * @return  The resulting string
     * @since 1.5
     */
    public String replace(CharSequence target, CharSequence replacement) {
        return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
                this).replaceAll(Matcher.quoteReplacement(replacement.toString()));
    }
replaceAll()
/**
     * Replaces each substring of this string that matches the given <a
     * href="../util/regex/Pattern.html#sum">regular expression</a> with the
     * given replacement.
     *
     *  翻译:
     *  1.将字符串中匹配正则表达式的子串替换为给定的序列
     
     * <p> An invocation of this method of the form
     * <i>str</i>{@code .replaceAll(}<i>regex</i>{@code ,} <i>repl</i>{@code )}
     * yields exactly the same result as the expression
     *
     * <blockquote>
     * <code>
     * {@link java.util.regex.Pattern}.{@link
     * java.util.regex.Pattern#compile compile}(<i>regex</i>).{@link
     * java.util.regex.Pattern#matcher(java.lang.CharSequence) matcher}(<i>str</i>).{@link
     * java.util.regex.Matcher#replaceAll replaceAll}(<i>repl</i>)
     * </code>
     * </blockquote>
     *
     *<p>
     * Note that backslashes ({@code \}) and dollar signs ({@code $}) in the
     * replacement string may cause the results to be different than if it were
     * being treated as a literal replacement string; see
     * {@link java.util.regex.Matcher#replaceAll Matcher.replaceAll}.
     * Use {@link java.util.regex.Matcher#quoteReplacement} to suppress the special
     * meaning of these characters, if desired.
     *
     * @param   regex
     *          the regular expression to which this string is to be matched
     * @param   replacement
     *          the string to be substituted for each match
     *
     * @return  The resulting {@code String}
     *
     * @throws  PatternSyntaxException
     *          if the regular expression's syntax is invalid
     *
     * @see java.util.regex.Pattern
     *
     * @since 1.4
     * @spec JSR-51
     */
    public String replaceAll(String regex, String replacement) {
        return Pattern.compile(regex).matcher(this).replaceAll(replacement);
    }

首先分析replaceAll()执行的流程
  • (1)Pattern.compile(regex)生成一个正则表达式的编译表示Pattern
  • (2)Pattern对象调用matcher(this)得到一个Matcher对象
  • (3)Matcher对象replaceAll(replacement)返回一个字符串对象
这个方法的时间复杂度和空间复杂度体现在哪儿呢?
  • (1)生成正则表达式对象时
public static Pattern compile(String regex) {
        return new Pattern(regex, 0);
    }
/**
     * This private constructor is used to create all Patterns. The pattern
     * string and match flags are all that is needed to completely describe
     * a Pattern. An empty pattern string results in an object tree with
     * only a Start node and a LastNode node.
     *  翻译:
     *  (1)这个私有的构造器是用于创建所有的Patterns
     *  (2)pattern string 和 match flags用于描述一个Pattern对象
     *  (3)一个空的Pattern string 导致创建一个只有开始节点和结束节点的对象树
     *  
     */
    private Pattern(String p, int f) {
        pattern = p;
        flags = f;

        // to use UNICODE_CASE if UNICODE_CHARACTER_CLASS present
        if ((flags & UNICODE_CHARACTER_CLASS) != 0)
            flags |= UNICODE_CASE;

        // Reset group index count
        capturingGroupCount = 1;
        localCount = 0;

        if (pattern.length() > 0) {
        //调用这个方法
            compile();
        } else {
            root = new Start(lastAccept);
            matchRoot = lastAccept;
        }
    }
/**
     * Copies regular expression to an int array and invokes the parsing
     * of the expression which will create the object tree.
     *  翻译:
     *  (1)将正则表达式复制为一个int类型的数组
     *  (2)将正则表达式复制到一个int数组,并调用将创建对象树的表达式的解析。
     */
    private void compile() {
        //此处省略n行生成树结构的核心代码
    }
  • (2)在上述生成一个Pattern的情况下(简单理解为一棵树),调用matcher()方法,将原字符串作为参数传递进去进行相应的匹配
/**
     * Creates a matcher that will match the given input against this pattern.
     *  翻译:
     *  (1)创建一个将input和pattern进行匹配的Matcher对象
     * @param  input
     *         The character sequence to be matched
     *
     * @return  A new matcher for this pattern
     */
    public Matcher matcher(CharSequence input) {
        if (!compiled) {
            synchronized(this) {
                if (!compiled)
                    compile();
            }
        }
        Matcher m = new Matcher(this, input);
        return m;
    }
  • (3)Matcher对象调用自身的replaceAll()方法
public String replaceAll(String replacement) {
        reset();
        boolean result = find();
        if (result) {
            StringBuffer sb = new StringBuffer();
            do {
                appendReplacement(sb, replacement);
                result = find();
            } while (result);
            appendTail(sb);
            return sb.toString();
        }
        return text.toString();
    }
主要的时间复杂度在于生成一个树结构
replace()方法的区别在于一开始传入的就是一个字符串,不用生成一个正则表达式树形结构,其余的过程都是差不多的
(2)concat()
参数值的长度为0,直接返回原字符串,否则先创建一个新的字符数组buf(长度为原字符串的长度+拼接的字符串长度),调用参数值的getChars()方法将自身复制到buf中,最终返回一个新的字符串
public String concat(String str) {
        int otherLen = str.length();
        if (otherLen == 0) {
            return this;
        }
        int len = value.length;
        char buf[] = Arrays.copyOf(value, len + otherLen);
        str.getChars(buf, len);
        //重点,返回新字符串
        return new String(buf, true);
    }
重载的两个方法最终都调用的是System.arraycopy()方法
//两个参数,目标数组,目标数组的初始下标
    void getChars(char dst[], int dstBegin) {
        System.arraycopy(value, 0, dst, dstBegin, value.length);
    }
    
//四个参数,源数组的开始、结尾,目标数组,目标数组的初始下标 
    public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
        if (srcBegin < 0) {
            throw new StringIndexOutOfBoundsException(srcBegin);
        }
        if (srcEnd > value.length) {
            throw new StringIndexOutOfBoundsException(srcEnd);
        }
        if (srcBegin > srcEnd) {
            throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
        }
        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
    }
(3)split(String regerx, int limit)
  • limit=0,表示按照正则表达式分割无数次,最终省略最后的空字符串
  • limit>0,分割n-1次数
  • limit<0,分割无数次,不省略最后的空字符串

2. 树结构-AVL树,红黑树

总结一句:红黑树是近似平衡的二叉树,能够在增删的过程中保持较高的搜索效率,相较于平衡二叉树的绝对平衡(每个节点的平衡因子为-1,0,1),红黑树由于增删引起的自旋次数更少。
[1]马博韬,孙鹏,朱小勇.红黑树算法研究综述[J].网络新媒体技术,2018,7(04):56-62.

3.字典树

字典树又称前缀树,典型应用是用于统计、排序、保存大量的字符串;利用字符串的公共前缀减少查询时间,最大限度的减少比较的次数。
(1)根节点不保存元素,除根节点以外的其他节点只保存一个字符
(2)从根节点到某节点经过的字符连接形成的字符串为该节点的字符串
(3)每个节点保存的字符都不相同

4.Java中的浅拷贝和赋值操作

首先分清基本数据类型和引用数据类型
赋值操作:基本数据类型,直接把内存中的值给新的变量,使用==输出true;引用数据类型,可以理解为在栈中并没有创建一个新的变量,仅仅是给原来的引用起了一个别名,二者都是指向同一个对象,使用 == 输出true
浅拷贝操作:前置条件,被拷贝的类实现Cloneable接口,重写clone()。拷贝操作可以理解为在栈中有了一个新的变量,包含的内容(基本数据类型的话就是进行复制,引用数据类型的话就是进行引用),使用==进行比较得到false
深拷贝顾名思义就是复制一个新的对象,这个对象对原对象的基本数据类型和引用数据类型都进行一份拷贝,最终得到两个在内存上完全独立的两个对象

5. 如何保证线程的顺序执行

(1)使用join(),某线程对象调用该方法,该对象所属线程会等待其执行完之后再往后执行
 /**
     * Waits for this thread to die.
     *  翻译:等待当前线程执行结束
     
     * <p> An invocation of this method behaves in exactly the same
     * way as the invocation
     *
     * <blockquote>
     * {@linkplain #join(long) join}{@code (0)}
     * </blockquote>
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    public final void join() throws InterruptedException {
        //实际调用了join(int millis)
        join(0);
    }
/**
     * Waits at most {@code millis} milliseconds for this thread to
     * die. A timeout of {@code 0} means to wait forever.
     *  翻译:
     *      1.最多等待millis线程结束
     *      2.0 意味着永远等待
     *
     * <p> This implementation uses a loop of {@code this.wait} calls
     * conditioned on {@code this.isAlive}. As a thread terminates the
     * {@code this.notifyAll} method is invoked. It is recommended that
     * applications not use {@code wait}, {@code notify}, or
     * {@code notifyAll} on {@code Thread} instances.
     *
     * @param  millis
     *         the time to wait in milliseconds
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    public final synchronized void join(long millis)
    throws InterruptedException {
        //一进入这个方法就设置一个基础的时间标
        long base = System.currentTimeMillis();
        //当前时间距base的时间间隔
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
        // public final native boolean isAlive();
        //调用了本地方法,判断当前线程是否存活
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                //delay<0说明已经超过了预设的时间
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                //还需要求一次now,用作下一次循环的判断
                now = System.currentTimeMillis() - base;
            }
        }
    }
    /*
     * Causes the current thread to wait until either another thread invokes the
     * {@link java.lang.Object#notify()} method or the
     * {@link java.lang.Object#notifyAll()} method for this object, or a
     * specified amount of time has elapsed.
     *  翻译:
     *  使得当前线程等待,直到其他线程调用notify()或者notifyAll()唤醒当前线程,或者达到了预设的时间
     *
     *  线程以三种方法成为对象监视器的所有者:
     *  1.调用改对象的同步方法
     *  2.执行该对象上的synchronized修饰的语句块
     *  3.对于类型为class的对象,执行该类的同步方法
     * 
     */
     public final native void wait(long timeout) throws InterruptedException;
应该注意的是,如果要使得多个线程在某个主线程里面有顺序地执行(下面的线程数量太少,可以在线程里面加一个循环或是增加线程的数量,使得结果更明显)
- 可以在各自的run()方法里面使用join()方法
public static void main(String[] args) {
        final Thread T1 = new Thread(new Runnable() {
            public void run() {
                System.out.println("T1 run");
                for(int i=0;i<100;i++){
                    System.out.println("T1 is running" + i);
                }
                System.out.println("T1 run end");
            }
        });
        final Thread T2 = new Thread(new Runnable() {
            public void run() {
                System.out.println("T2 run");
                try{
                    T1.join();
                }catch (Exception e){
                    e.printStackTrace();
                }
                System.out.println("T2 run end");
            }
        });
        Thread T3 = new Thread(new Runnable() {
            public void run() {
                System.out.println("T3 run");
                try{
                    T2.join();
                }catch (Exception e){
                    e.printStackTrace();
                }
                System.out.println("T3 run end");
            }
        });
        T1.start();
        T2.start();
        T3.start();
    }
  • 也可以在主线程里面使用start()和join()方法,但是需要先start()启动线程,再使用join()方法,因为由上述join()源码可知会先调用isAlive()判断线程是否存活,不存活的话不会执行wait()方法,3个线程还是处于竞争状态,不会顺序执行。
public static void main(String[] args) throws InterruptedException {
        final Thread T1 = new Thread(new Runnable() {
            public void run() {
                System.out.println("T1 run");
                for(int i=0;i<100;i++){
                    System.out.println("T1 is running" + i);
                }
                System.out.println("T1 run end");
            }
        });
        final Thread T2 = new Thread(new Runnable() {
            public void run() {
                System.out.println("T2 run");
                for(int i=0;i<100;i++){
                    System.out.println("T2 is running" + i);
                }
                try{
                    //T1.join();
                }catch (Exception e){
                    e.printStackTrace();
                }
                System.out.println("T2 run end");
            }
        });
        Thread T3 = new Thread(new Runnable() {
            public void run() {
                System.out.println("T3 run");
                for(int i=0;i<100;i++){
                    System.out.println("T3 is running" + i);
                }
                try{
                    //T2.join();
                }catch (Exception e){
                    e.printStackTrace();
                }
                System.out.println("T3 run end");
            }
        });
        T1.start();
        T1.join();
        T2.start();
        T2.join();
        T3.start();
        T3.join();

    }
(2)使用进程间通信机制,基于wait()方法
private static boolean T2Run = false; //标识位,用来通知T2线程执行

    private static boolean T3Run = false;


    public static void main(String[] args) {

        Object lock1 = new Object();
        Object lock2 = new Object();

        Thread T1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock1){
                    System.out.println("T1 run");
                    for(int i=0;i<100;i++){
                        System.out.println("T1 is running" + i);
                    }
                    //t1 线程通知t2执行
                    T2Run = true;
                    lock1.notify();
                }
            }
        });
        Thread T2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock1){
                    if(!T2Run){
                        System.out.println("T2 wait");
                        try {
                            lock1.wait();
                        } catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                    System.out.println("T2 run");
                    for(int i=0;i<100;i++){
                        System.out.println("T2 is running" + i);
                    }
                    //t2 线程通知t3执行
                    synchronized (lock2){
                        T3Run = true;
                        lock2.notify();

                    }
                }
            }
        });

        Thread T3 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock2){
                    if (!T3Run){
                        System.out.println("T3 wait");
                        try {
                            lock2.wait();
                        } catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                    System.out.println("T3 run");
                    for(int i=0;i<100;i++){
                        System.out.println("T3 is running" + i);
                    }
                }
            }
        });
        T1.start();
        T2.start();
        T3.start();
    }
- 上述代码中最核心的变量是标志变量,两个对象锁变量,最核心的方法是notify(),wait(),注意这两个方法的调用者是锁对象,分别使得等待改对象的线程中的任意一个被唤醒、使得当前线程等待,直到被唤醒或者达到预设的时间,由源码注释可知
- 重点注意的是调用wait()方法的对象应该被同步
synchronize(someobject){
   someobject.wait();
}
/**
     * Wakes up a single thread that is waiting on this object's
     * monitor. If any threads are waiting on this object, one of them
     * is chosen to be awakened. The choice is arbitrary and occurs at
     * the discretion of the implementation. A thread waits on an object's
     * monitor by calling one of the {@code wait} methods.
     */
     notify
     
     /**
     * Causes the current thread to wait until either another thread invokes the
     * {@link java.lang.Object#notify()} method or the
     * {@link java.lang.Object#notifyAll()} method for this object, or a
     * specified amount of time has elapsed.
     * <p>
     * The current thread must own this object's monitor.
     * <p>
     */
     wait
(3)使用线程池newSingleThreadExecutor,核心线程数和最大线程数都为1,但是不推荐使用,因为阻塞队列没有限制,容易导致内存溢出
/**
     * 线程池
     * 核心线程数:1
     * 最大线程数:1
     * 在日常中不建议使用newSingleThreadExecutor,因为阻塞队列个数没有限制,会导致内存溢出
     *
     */
    static ExecutorService executorService = Executors.newSingleThreadExecutor();


    public static void main(String[] args) {
        Thread T1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("T1 run");
            }
        });
        Thread T2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("T2 run");
            }
        });
        Thread T3 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("T3 run");
            }
        });
        executorService.submit(T1);
        executorService.submit(T2);
        executorService.submit(T3);
        executorService.shutdown();
    }
(4)使用CountDownLatch,相比于wait()的优点就是可以等待多个线程执行完毕之后再执行某个线程
  • CountDownLatch(int count); //构造方法,创建一个值为count 的计数器
  • await();//阻塞当前线程,将当前线程加入阻塞队列。
  • countDown();//对计数器进行递减1操作,当计数器递减至0时,当前线程会去唤醒阻塞队列里的所有线程。
/**
     * 计数器1 用于T1线程通知T2线程
     * 注意:这里个数都设置成立1 ,当T1执行完成后,执行countDown,来通知T2线程
     */
    static CountDownLatch countDownLatch1 = new CountDownLatch(1);

    /**
     * 计数器2 用于T2线程通知T3线程
     */
    static CountDownLatch countDownLatch2 = new CountDownLatch(1);

    public static void main(String[] args) {
        Thread T1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("T1 run");
                countDownLatch1.countDown();
                System.out.println("T1 countDown finish");
            }
        });
        Thread T2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    countDownLatch1.await();
                }catch (Exception e){
                    e.printStackTrace();
                }
                System.out.println("T2 run");
                countDownLatch2.countDown();
            }
        });
        Thread T3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    countDownLatch2.await();
                }catch (Exception e){
                    e.printStackTrace();
                }
                System.out.println("T3 run");
            }
        });
    }
(5)CyclicBarrier,多个线程达到某一个节点之后,再一起继续往下执行
  • CyclicBarrier(int parties) //构造方法,参数表示拦截的线程的个数
  • CyclicBarrier(int parties, Runnable barrierAction) //也是构造方法,可以通过后面的参数,这是线程的优先级
  • await() //告诉CyclicBarrier自己已经到达同步点,然后当前线程被阻塞,当所有线程都到达同步点(barrier)时,唤醒所有的等待线程,一起往下继续运行,可根据参数barrierAction决定优先执行的线程
/**
     * 设置2个线程互相等待,直到到达同一个同步点,再继续一起执行。T1不执行完,T2就永远不会执行
     */
    static CyclicBarrier cyclicBarrier1 = new CyclicBarrier(2);

    /**
     * 设置2个线程互相等待,直到到达同一个同步点,再继续一起执行。
     */
    static CyclicBarrier cyclicBarrier2 = new CyclicBarrier(2);

    public static void main(String[] args) {
        Thread T1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("T1 run");
                try{
                    cyclicBarrier1.await();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        });
        Thread T2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    cyclicBarrier1.await();
                }catch (Exception e){
                    e.printStackTrace();
                }
                System.out.println("T2 run");
                try{
                    cyclicBarrier2.await();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        });
        Thread T3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    cyclicBarrier2.await();
                }catch (Exception e){
                    e.printStackTrace();
                }
                System.out.println("T3 run");
            }
        });

        T1.start();;
        T2.start();
        T3.start();
    }
(5)使用condition 和 Semaphore信号量 略

6.Java中的基本IO

  • 此处引用:https://cloud.tencent.com/developer/article/1785545
  • 总结的较为精辟的就是:Reader,Writer;InputStream,OutputStream,后者可以处理所有的文件,但是因为是以字节为单位进行操作,遇到某些字符是超过1个字节的(汉字)就会出现乱码,所以出现了Reader,Writer。
  • 字节流和字符流的转换使用转换流;提高读写的速度使用缓冲流或使用数组,因为涉及到用户态和核心态的切换,如果能增加一次读写的量,就会减少转换的次数
  • 在写文件之后需要使用flush(),close(),前者将缓冲区中的数据写出(默认一个缓冲区的数据写满之后会自动进行写出,但是最后的数据没有达到缓冲区的大小的话就不会自动写,需要强制flush()),后者的话是减少资源的占用,所以需要进行手动关闭。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值