栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

2021-10-17

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

2021-10-17

1. java.lang.String (1) replace()和replaceAll() 废话不多说直接上源代码 replace()
    public String replace(CharSequence target, CharSequence replacement) {
        return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
                this).replaceAll(Matcher.quoteReplacement(replacement.toString()));
    }
replaceAll()
    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);
    }
    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;
        }
    }
    private void compile() {
        //此处省略n行生成树结构的核心代码
    }
  • (2)在上述生成一个Pattern的情况下(简单理解为一棵树),调用matcher()方法,将原字符串作为参数传递进去进行相应的匹配
    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(),某线程对象调用该方法,该对象所属线程会等待其执行完之后再往后执行
 
    public final void join() throws InterruptedException {
        //实际调用了join(int millis)
        join(0);
    }
    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;
            }
        }
    }
    
     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();
}
     notify
     
     
     wait
(3)使用线程池newSingleThreadExecutor,核心线程数和最大线程数都为1,但是不推荐使用,因为阻塞队列没有限制,容易导致内存溢出
    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时,当前线程会去唤醒阻塞队列里的所有线程。
    static CountDownLatch countDownLatch1 = new CountDownLatch(1);

    
    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决定优先执行的线程
    static CyclicBarrier cyclicBarrier1 = new CyclicBarrier(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()),后者的话是减少资源的占用,所以需要进行手动关闭。
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/333047.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号