JAVA代码实现字符串匹配(一)——BF、KMP

2023-05-16

话不多说,直接进入主题:
        题目描述:给定两个字符串text和pattern,请你在text字符串中找出pattern字符串出现的第一个位置(下标从0开始),如果不存在,则返回-1;
        LeetCode字符串匹配的题目:https://leetcode-cn.com/problems/implement-strstr/
        举个例子:

字符串text(以下简称T,也叫主串)T = “ABABABABCABAAB”;
字符串pattern(以下简称P,也叫模式串)P = “ABABCABAA”;

        字符串匹配的问题有很多种方法,最简单的就是暴力匹配(BF),也是最容易想到的。首先还是得先介绍以下BF法,只有清楚了BF的缺点才能更好的理解后续的优化算法。

        1. 暴力匹配(BF)

在这里插入图片描述
在这里插入图片描述
。。。
在这里插入图片描述

        首先,将T和P左对齐,定义指针i用于遍历T,指针j用于遍历P,每次比较指针i和j所指的元素是否相同,相同就同时移动两个指针,当碰到不同时(也就是“坏字符”),将j指针回到P的起始位置,i指针移动到上一次的起始位置的下一位:
在这里插入图片描述
        以此类推,直到j指针移动到P的最后一位,说明在T中找到了P的位置,此时i的位置指向了T中P字符串的末尾,因此起始位置应该是i-j;

/**
 1. BF
 2. @param ts 主串
 3. @param ps 模式串
 4. @return 如果找到,返回在主串中第一个字符出现的下标,否则为-1
 */
public static int bf(String ts, String ps) {
    char[] t = ts.toCharArray();
    char[] p = ps.toCharArray();
    int i = 0; // 主串的位置
    int j = 0; // 模式串的位置
    while (i < t.length && j < p.length) {
       if (t[i] == p[j]) { // 当两个字符相同,就比较下一个
           i++;
           j++;
       } else {
           i = i - j + 1; // 一旦不匹配,i后退
           j = 0; // j归0
       }
    }
    if (j == p.length) {
       return i - j;
    } else {
       return -1;
    }
}

       在使用BF暴力匹配字符串的时候,每次遇到“坏字符”,就会回溯i指针,使其回到上一次起始位置的下一个位置上重新进行匹配,这个过程浪费了很多不必要的匹配,比如这种极端情况:
在这里插入图片描述
。。。
在这里插入图片描述
在这里插入图片描述
       不难发现,这种情况时,模式串P的前8个S参与很多次重复的匹配,导致匹配的效率大打折扣。假设主串T的长度为n,模式串长度为m,可以计算出BF算法的时间复杂度为O(mn)。

2. 如何优化暴力匹配这种方式,减少重复匹配的次数?

       其实,通常情况下,在匹配过程中,碰到“坏字符”时,我们潜意识中不会那么笨的把i指针回溯到上一次的起始位置,再往前挪一位,再进行比较,而是直接会这么操作:
在这里插入图片描述
在这里插入图片描述
       正是这种“潜意识”告诉我们没有必要一步一步往下比较,在“坏字符”出现之前的字符串肯定是已经匹配上的了,不妨我们将这些已经匹配的字符串记为K。这样主串T和模式串P中各会有一个K串。匹配过程中,每次出现“坏字符”的时候,我们只需要将主串中K的后缀和模式串中K的前缀对应上,这样就避免了指针i的回溯。
在这里插入图片描述
       注意这里说的主串中K的后缀和模式串中K的前缀对应上这句话,首先要理解一个字符串的前缀和后缀:
       前缀:这里说的字符串的前缀是指这个字符串除掉最后一个字符,剩下字符的有序集合,例如ABAB的前缀是{A,AB,ABA};
       后缀:这里说的字符串的后缀是指这个字符串除掉第一个字符,剩下字符的有序集合,例如ABAB的后缀是{B,AB,BAB};
       其次,要理解这句话中说的对应这个词的含义:这里是指前缀和后缀集合中公共最长字符串。
       那么,还是以ABAB为例,主串中K的后缀集合是{B,AB,BAB},模式串中K的前缀集合是{A,AB,ABA},连个集合中公共的最长字符串是AB,那么对应上的意思就是:
在这里插入图片描述
在这里插入图片描述
       到这,会发现指针i并没有移动,但是指针j的位置移动了,移动到哪了呢?正好是K的公共最长字符串长度在模式串P的下标位置上,例如此时K的最长公共字符串AB的长度是2,那么指针j的位置移动到了P下标为2的位置上。
       接下来,会继续拿指针i和j位置上的字符进行对比,碰到“坏字符”,会和上面说的过程一样,找出K中公共最长字符串,然后对应上,继续判断。。。
在这里插入图片描述
在这里插入图片描述
       此时,指针j匹配到最后发现都匹配上了,也就是说在主串T中找到了模式串第一次出现的位置,需要将主串T中第一次出现模式串P的起始下标返回:
在这里插入图片描述
       注意此时i的位置,其实是指在T中P串的末尾处,所以应该返回的是i-j。

       到此,这就是KMP算法的大体步骤,相信你一眼就能看出,KMP算法的核心之处,就是这个公共最长字符串,也就是大家所说的前缀表(next数组)如何生成。

3.如何生成前缀表(next数组)

       既然到这了,是时候放出b站的一个大牛对KMP算法的教学视频了:

  •        https://www.bilibili.com/video/BV1Px411z7Yo
  •        https://www.bilibili.com/video/BV1hW411a7ys

       这位大佬对KMP的算法讲解的算是很透彻了,我研究了大半天都没搞懂的代码,看了他讲解的视频,恍然大悟,直呼666。
       当然了,我也在此说一下是怎么生成这个前缀表的。首先,要搞清楚一个事情,我们为什么要生成这个前缀表(next数组)?其次,这个前缀表(next数组)到底是个什么结构,它的作用是什么?好的,现在开始一一解答。

       3.1.为什么要生成这个前缀表(next数组)?

       从上面的过程中不难发现,我们之所以“潜意识”会直接将模式串P往后挪到一个合理的位置,再进行匹配,从本质上来说,就是我们找到了K串中的公共最长字符串,那么我们为什么要找这个公共最长字符串呢?我们用到了这个公共最长字符串的哪些信息可以让我们将模式串后移到合理的位置再进行匹配呢?答案是:长度。我们费尽心思的拿到这个公共最长字符串的目的有其二:1:我们不想让指针i回溯,而是当停在出现“坏字符”的位置,通过移动模式串P到一个合理的位置后,然后从指针i这个位置继续往后匹配。怎么体现合理的位置呢?就是要保证当指针i停留在“坏字符”处不动时,如何移动指针j,使其指针j前面已经匹配的字符最大程度上不用再重复匹配了,这个最大程度的长度就是公共最长字符串的长度。2:就是通过公共最长字符串长度来确定指针j应该移动到哪个位置,然后重新和指针i进行匹配。
       这就是为什么要生成这个前缀表(next数组),好像一不小心把前缀表(next数组)的作用也说了。。。

       3.2.前缀表(next数组)是个什么结构?

       通过上面的分析,我们仿佛发现,好像只需要公共最长字符串的长度就行了。但是,仔细琢磨会发现,这个公共最长字符串并不是固定的,他是一个动态的过程,因为这个K串可能会是一个字符、两个字符、三个字符…,最多也就是模式串P的长度。因为谁也不能保证,在匹配的过程中,在哪个位置碰到“坏字符”。如果在第一个位置碰到“坏字符”,那K串就是"",空串,如果在P的最后一个字符上碰到“坏字符”,那K串就是P串去掉最后一个“坏字符”剩下的串。所以,我们已经知道这个前缀表(next数组)其实就是K串与下一次指针j移动到合理位置的对应关系。这个合理位置就是下一次指针j需要移动到以公共最长字符串的长度值为P数组下标的位置上。说起来可能比较绕,直接上一个实际的例子来看吧:
       T = “ABABABABCABAAB”
       P = “ABABCABAA”
       
       当i和j匹配到A和C时,出现“坏字符”,这个时候K串是ABAB,此时公共最长字符串是AB,长度len=2,那么我们只需要将指针j移动到P串下标为2的位置上重新开始匹配就行了,也就是下图中A的位置:
在这里插入图片描述
       此时,主串T中K串的后缀AB正好和模式串P中K串的前缀AB对应上了,保证了j指针这次移动后,j之前的元素已经是匹配好了的。那么为什么j指针移动到这个位置就一定能保证j之前的元素四匹配好了的呢?其实这就是一个T和P前后缀对应后指针j停留在哪的问题。所以就很好理解了。因为为了让T和P的前后缀对应上,我们只需要把P向后移动len个单位就可以了,这个len就是公共最长字符串的长度,程序中如何体现把P往后移动呢,很简单,就是把指针j往前移动。因为数组的下标是从0开始的,所以j往前移动len个单位,自然最后的位置就是停留在公共最长字符串AB的下一个位置上,也就是P[len]这个位置上了。
       花了这么大精力解释完这个问题后,终于到了最关键的步骤,如何生成这个前缀表(next数组)?

       3.2如何生成前缀表(next数组)

       在生成next数组(以下都叫next数组了)之前,我们已经明确一种对应关系,也就是K串的长度和下一次指针j移动到什么位置的对应,其实通过上面的解释,这种对应关系可以换种说法了:K串的长度和len的对应关系。我们直接把next数组的下标当做K串的长度,下标对应的值当做len,这种对应关系就建立了。到这,那就好办了,因为主串的K和模式串的K是一样的,那直接开始遍历模式串P不就行了。是的,实际也是这么干的,我们还是通过图的形式展示一下:
在这里插入图片描述
       我们一轮一轮分析:
       第一轮:因为K串是"",没有公共最长前后缀,所以len=0,next[0]=0;
       第二轮:多了一个B字符,此时K串是A,显然单个字符也没有公共最长前后缀,所以len=0,next[1]=0;
       第三轮:多了一个字符A,此时K串是AB,前缀是{A,AB},后缀是{A,BA},公共最长前后缀是A,len=1,next[2]=1;注意!!!从这里开始,我们需要观察了。每一轮都会多一个字符,多出来的字符直接影响到这一轮的len是多少。如果我们每一轮都重新去判断K串的公共最长前后缀,那太繁琐了。经过观察(我是没想到,大佬们想到了。。。),每一轮多出来的这个字符串只需要跟上一轮公共最长前后缀的下一个字符对比,就可以判断len需不需要加1了。这句话很重要!!!(据说用到了动态规划的思想,咱也不知道,咱也不敢问)。还是以第三轮来说吧,这一轮多出来的字符是A,我标红了,因为上一轮len=0,也就是说上一轮没有公共最长前后缀,没有的话,那公共最长前后缀的下一个字符是谁呢?答案是第二轮前面的那个A,我标绿了。为什么是A呢,我想也很好理解,我们可以想象,第二轮没有公共最长前后缀,也就是一个"",空串,我们假设这个空串也是一个字符,他就在第二轮A的前面,那他下一个字符就是A了,这么想应该就好理解了。也就是说我们比较P[i]和P[len]是否相等,如果相等,说明这一轮新加的字符和上一轮公共最长前后缀的下一个位置的字符相同,那就说明这一轮的len可以加1了。所以第三轮len=1,next[2]=1;
       第四轮:新加了一个字符B,那么P[3]和P[1]相等,说明公共最长前后缀又变长了,len=2,next[3]=2;
       第五轮:这轮多出来的字符是C,P[4]和P[2]不相等了。之前都是判断相等的情况len++,这回不相等了,怎么办。说明我拿上一轮的公共最长前后缀的下一位和这轮多出来的字符对比不行了,那就得和上上一轮的len做对比了。这种做法就像是在验证,多出来的这个字符已经不能让这一轮的len++了,会不会直接就是0了呢,得赶紧去看上上一轮公共最长前后缀的下一位,那从图中来看,第四轮的公共最长前后缀是AB,下一位是A,此时A!=C,所以我们去拿上上一轮的len,第三轮的公共前后缀的下一位是B,那我们是跟这个B做对比吗。不是!这块也是最难理解的点。仔细观察我们会知道,第五轮,在这个C没加进来之前,公共最长前后缀已经是AB了,由于C和第四轮的公共最长前后缀的下一位A不相同,导致第五轮公共最长前后缀没有增1,没有增1不可怕,可怕的是每一轮只能多出来一个字符,如果这个字符没有让公共最长前后缀加1,那必然已经让上一轮的公共最长前后缀失效了,比如说第四轮K串是ABAB,公共最长前后缀是AB,但是第五轮进来的是C不是A,这样第五轮的公共前后缀已经不可能是ABA了,也不可能跟第四轮一样是AB了,所以去第三轮判断中间的B是没有意义的(因为第三轮中间的B实际是用来判断公共最长前后缀AB的),所以应该直接去看第二轮的A是不是和这个C匹配。这种跨这么多维度去判断,应该怎么实现呢,在之前说的那个b站大佬讲解的视频当中,他对这块也是直接上来就说出了解决方案,那就是当P[i]!=P[len]时,len=next[len-1];然后重复本轮循环。拿第五轮来说,当C和第四轮的第三个元素A对比,发现不相等时,此时len还是第四轮的值len=2;这时候直接跨过第三轮,len=next[2-1]=0;然后再让这个C去和公共最长前后缀”“的下一位去判断,此时len=0了,公共最长前后缀已经是空串了。下一位当然就是首字母A了,C和A判断,发现还不相等,此时前面已经不会有公共最长前后缀了,那第五轮的公共最长前后缀只能是空串了,所以第五轮的len=0,next[4]=0;注意!!!这块又需要注意了,因为len=next[len-1]这个-1的操作,很可能会出现-1的情况,比如咱们直接看第二轮,当多出来的字符B和上一轮公共最长前后缀的下一位A不一样,按理来说也应该len=next[len-1],但是此时第一轮的len=0,所以这块就会出现next[-1]数组下标越界的情况,所以还需要有一个限定条件,那就是如果P[i]!=P[len],并且len=0的时候,那就不需要len=next[len-1]这个操作了,直接将本轮的next[i]=0就行了。
       第六、第七、第八轮:情况和第三轮、第四轮一样。
       第九轮:经过了上述的分析,咱们具体来看一下第九轮的情况:首先,第九轮多出来的字符是A,第八轮的公共最长前后缀是ABA,len=3,next[7]=3;1、P[8]!=P[3],说明第九轮的len不会加1了,len!=0,所以len=next[len-1]=next[3-1]=1,此时P[8]!=P[1],又不相等,此时len=1!=0,所以len=next[len-1]=next[1-1]=0,此时P[8]==P[0],终于相等了,len++,next[8]=1。
       至此,模式串遍历完毕,我们得到了next数组next={0,0,1,2,0,1,2,3,1};后续我们进行KMP算法就方便多了,比如说在模式串P[j]处碰到”坏字符“,那直接j=next[j],就将指针移动到了新的位置,并且在j之前的字符就是公共最长前后缀,都是已经匹配通过的,然后和指针i继续往后判断即可。但是目前这个next数组还不是最终形态,存在两个问题:1.第九轮已经是P串本身了,实际匹配的时候,指针j如果在模式串P的最后一个字符上匹配通过,那就说明模式串P在主串T中找到了,所以K=P这中情况实际上压根用不上,所以next数组的最后一个元素可以去除;2.next[0]和next[1]永远都会是0,因为一个字符不会有公共最长前后缀,两个字符也不会有公共最长前后缀,那怎么区分这两种情况呢。我们不妨让next[0]=-1,这样就区分开了,为什么要设置成-1,是有原因的,那就是匹配过程中碰到”坏字符“时,理论上是j=next[j],但是如果next[0]和next[1]都是0的话,这里就出现死循环了。其实这是一种临界场景,就是当j指针已经移动到P串的起始位置了,但是发现P[0]和T[i]还是不相等,这时候已经不能再移动j了,说明已经没有公共最长前后缀了,必须将i的指针也往前移动一位,那如果我们把next[0]设置成了-1,那碰到这种临界情况,就更好理解了,当j已经到P[0]的位置,,发现P[0]还是不等于T[i],此时j=next[0]=-1,接下来判断如果j=-1,说明遇到这种临界情况了,我们需要将i和j同时往后移动一位,i++,j++,此时j又回到P[0]位置,而i也往后移动了一位,继续判断P[J]和T[i]即可。
       基于上述分析,我们只需要将next数组整体后移一位,挤掉最后一个元素,并且将next[0]赋值为-1,就是一个最终形态的next数组了。说了这么多,终于到代码了,其实刚才分析了这么多,都是代码的每一个细节,这时候理解代码就好接受多了吧。

/**
     * 获取前缀表。数组下标代表当前字符串的长度,值代表当前字符串前缀和后缀共有最大字符串的长度
     * @param pattern
     * @return
     */
    public static int[] getNext(String pattern){
        // 先求出字符串的前缀表
        char[] charArr = pattern.toCharArray();
        int[] next = new int[charArr.length];
        // 因为字符串的第一个元素没有前后缀,所以共有最大字符串长度为0
        next[0] = 0;
        int len = 0;
        int i = 1;
        /*
            i   str          next[i]
            0   A            0
            1   AB           0
            2   ABA          1
            3   ABAB         2
            4   ABABC        0
            5   ABABCA       1
            6   ABABCAB      2
            7   ABABCABA     3
            8   ABABCABAA    1
        */
        while (i < charArr.length){
            // 1.举例:比如这次进来的字符串是上面的AB,此时上一次的共有字符串长度是len=0(因为上一次就一个A字符,没有共有字符串,当然是0),
            // 要想判断这次共有字符串长度会不会加1,只需要判断这次的字符串AB比上次字符串A多出来的字符(也就是B)是不是和上次共有字符串长度位置上的字符相等
            // 也就是charArr[1(i)] == charArr[0(len)]?,这里是不等,所以不能加1
            // 2.比如这次进来的是ABA,上一次是AB,那么多出来的这个A和上次AB的共有字符串长度位置(len=0)上的字符是否相等,显然charArr[0] == charArr[2],所以len++;
            // 3.再比如:这次进来的是ABAB,上一次是ABA,上一次的共有字符串长度是len=1,判断这次多出来的字符B是不是和charArr[1]相等,显然相等,len++;
            if(charArr[i] == charArr[len]){
                len++;
                next[i] = len;
                i++;
            }else{
                // 如果不相等,说明这次多出来的这个字符并没有让共有字符串的长度加1,而且,可能还会直接影响到上一次的共有字符串长度
                // 这里的做法是:因为多出来一个字符,而且charArr[i] != charArr[len],那这次已经不能拿上一次共有字符串位置上的字符来做比较了,必须拿上上一次的结果
                // 比如:这次进来的是ABABC,上一次是ABAB,上一次共有字符串是AB,len=2,那charArr[2(len)]是A,和这次的多出来的C已经不一样了,那上次的len已经不能作为判断依据了,
                // 必须拿上上一次的len,于是i不变,也就是说下一轮循环还是ABABC,但是len要拿上上一轮的长度,也就是AB这个字符串共有字符串的长度值,len=1,
                // 此时charArr[1(len)]是B,还是和C不相同,说明这次的len还是不能作为判断,于是i继续不变,下一轮还是ABABC,len拿A的共有字符串长度值,len=0,
                // 此时charArr[0(len)]是A,还是和C不相同,说明这次的len还是不能作为判断,理论上还得去那更早一次的len值,但是这时候有个临界情况,因为已经拿到第一次进来的len了,
                // len拿不到更早一次的值了,或者说到这已经没有共有字符串了,说明这次加多出来的字符C。彻底让这个字符串ABABC没有了共有字符串,也就是len=0,可以放心的将这一轮字符串
                // 的共有字符串长度设为0了,这轮len值设置完毕,i++,进行下一轮设置
                if(len > 0){
                    len = next[len-1];
                }else{
                    next[i] = len;
                    i++;
                }
            }
        }
        // 到此,前缀表已经设置完毕,但是有个问题,就是next[0]和next[1]的位置一直都是0,为了后续使用的方便,需要将""和只有一个字符的字符串共有前缀区别开,
        // 而且,对共有字符串来说,前缀表的最后一项就是字符串本身的共有字符串长度,这个在实际使用的时候没有意义,所以直接将整个前缀表往后平移一位,空出来的
        // next[0]赋值为-1
        for (int j = next.length  -1; j > 0; j--) {
            next[j] = next[j-1];
        }
        next[0] = -1;
//        for (int m = 0; m < next.length; m++) {
//            System.out.print(next[m] + "");
//        }
        return next;
    }

       终于把KMP算法中最核心的next数组生成问题说完了。。。
       最后一个问题,如何写KMP算法匹配字符串?

       4.KMP匹配字符串

       其实到这,就是临门一脚的事情了,大致说一下KMP的步骤吧

        * 循环遍历主串T和模式串P,指针i用来遍历T,指针j用来遍历j;
        * 当T[i]!=P[j]时,j=next[j],如果j=-1,i++,j++;
        * 当T[i]==P[j]时,i++,j++即可;
        * 循环结束后,判断j是否到了P的末尾,如果相等,则说明在T中找到了第一次出现P的位置,此时i也在T中P的末尾处,所以P在T的起始位置应该是i-j;如果不相等,返回-1;

       话不多说,赶紧附上代码

/**
     * KMP算法
     * @param text
     * @param pattern
     * @return
     */
    public static int findIndexForKMP(String text, String pattern){
        if(text == null || pattern == null){
            return -1;
        }
        if(pattern.length() == 0){
            return 0;
        }
        // 首先得到前缀表
        // 前缀表是一个一维数组,数组的下标表示遍历pattern的指针位置(j),数组的值表示当pattern的指针j和text的指针i对应的字符内容不同时(坏字符),j指针需要移动
        // 到pattern的位置
        int[] next = getNext(pattern);
        // 定义两个指针,i用来遍历text,j用来遍历pattern
        int i = 0;
        int j = 0;
        char[] charsT = text.toCharArray();
        char[] charsP = pattern.toCharArray();
        while(j < charsP.length && i < charsT.length){
            // 当指针i和j位置上的元素不匹配时,需要将j指针通过next数组移动到指定位置上
            if(charsT[i] != charsP[j]){
                j = next[j];
                // 如果j等于-1,说明j指针已经pattern的最前面了,并且已经没有共有字符串了,直接将j指针和i指针同时往后移动一位j++
                // 这里就能体现将next[0]设置成-1的巧妙了,这样j++,i++的时候就意味着把text当做一个全新的字符串,除去了[0,i)之前的元素,j=0也从pattern
                // 的起始位置开始匹配
                if(j == -1){
                  i++;
                  j++;
                }
            }else{
                //如果匹配上,那就正常进行下一个字符的匹配
                i++;
                j++;
            }
        }
        // 循环结束后,如果j和pattern长度相同,说明全部匹配完了,也就是在text中找到了第一次出现pattern的位置,这时候i指针已经到了pattern字符位置的最后一个字符的位置
        // 起始位置需要减掉pattern的长度
        if(j == charsP.length){
            return i - j;
        }
        return -1;
    }

       KMP算法是通过next数组来完成匹配的,如果P串的长度为m,则生成next数组的复杂度是O(m),T串的长度为n,则匹配的复杂度是O(n),最终整个算法的复杂度是O(m+n),确实是比暴力匹配复杂度低,但是这是人能想出来的事吗???只能说,你确实降低了计算机的复杂度,但是你提高了人脑的复杂度。。。我们发明计算机的目的不就是不想动脑吗!!!好了,开个小玩笑。。。这些大佬们还是很牛逼的。
       终于讲完了,如果有一些说的不对的地方,麻烦直接指出,或者有什么更好的理解,可以及时交流一下心得。
       最后还是得特别感谢一下b站的这位大佬,解决了我很多代码上的困扰,欢迎各位积极讨论。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JAVA代码实现字符串匹配(一)——BF、KMP 的相关文章

  • 如何在 JPA 中使用枚举

    我有一个电影租赁系统的现有数据库 每部电影都有一个评级属性 在 SQL 中 他们使用约束来限制该属性的允许值 CONSTRAINT film rating check CHECK rating text text OR rating tex
  • 抽象超类的默认接口方法

    可以说我有以下结构 abstract class A abstract boolean foo interface B default boolean foo return doBlah class C extends A implemen
  • 哪个类调用了我的静态方法?

    假设我有一个带有静态方法的 Java 类 如下所示 class A static void foo Which class invoked me 进一步假设 A 类有任意数量的子类 class B extends A class C ext
  • 不同的 JDK 更新会产生不同的 Java 字节码吗?

    假设场景 我有一个项目 其源合规性级别指定为 1 5 现在 我使用两种不同的 JDK 编译此项目 首先使用 JDK 6 Update 7 然后使用 JDK 6 Update 20 这两个不同的 JDK 是否会生成不同的 Java 字节代码
  • 如何将列表转换为地图?

    最近我和一位同事讨论了转换的最佳方式是什么List to Map在 Java 中 这样做是否有任何具体的好处 我想知道最佳的转换方法 如果有人可以指导我 我将非常感激 这是个好方法吗 List
  • 隐藏类的 System.out.print 调用

    我正在使用 java 库 jar 文件 该文件的作者放入了一堆System out print and System out printlns 有没有办法隐藏特定对象的这些消息 编辑 看起来jar文件似乎正在创建一堆线程 并且每个线程都有它
  • 在 Java 中填充布尔数组

    作为一名相当新手的 Java 程序员 我给自己设定了一个艰巨的挑战 尝试编写一个简单的文本冒险 不出所料 我已经遇到了困难 我试图为我的 Location 类提供一个属性来存储它包含的退出 我为此使用了一个布尔数组 本质上保存代表每个出口的
  • 将多个视频文件合并到一个文件中

    我有多个以相同帧速率和分辨率录制的视频 我想将两个视频合并为一个视频 因此结果文件将是大视频 我正在使用 MP4 解析器 api 并使用下面的代码 Movie countVideo new MovieCreator build Channe
  • 给定一个单词列表 - 在 java 中完成单词的好的算法是什么?权衡:速度/效率/内存占用

    我正在探索潜在的免费 付费应用程序的硬件 软件要求 最终目标是移动 Java 应用程序 该应用程序将从这个简单的目标开始 给定数据库中相关单词的列表 能够对单个字符串输入进行单词补全 换句话说 我已经知道数据库的内容 但算法的内存占用 速度
  • 使用 CrudRepository 进行自定义查询

    我想使用 CrudRepository 自定义查询 这是我的代码 Repository public interface CustomerRepository extends CrudRepository
  • 在java中是否可以使用反射创建没有无参数构造函数的“空白”类实例?

    我有一个没有默认构造函数的类 我需要一种方法来获取此类的 空白 实例 空白 意味着实例化后所有类字段都应具有默认值 如 null 0 等 我问这个问题是因为我需要能够序列化 反序列化大对象树 而且我无法访问该对象类的源 并且类既没有默认构造
  • 我们必须将 .class 文件放在 Tomcat 目录中的位置

    我必须把我的 class文件在 Tomcat 目录中 在我的 Java Complete Reference 书中 他们告诉将其放入C Program Files Apache Tomcat 4 0 webapps examples WEB
  • Java中的OR运算(BitSet.class)

    如何编写一个程序 该程序需要001010101110000100100 011100010001000011000 000000000010000000000100 作为输入 位 输出将是OR其中 3 个 OR 0 0 0 0 1 1 1
  • java.lang.IllegalArgumentException:addChild:子名称“/”不唯一

    java lang IllegalArgumentException addChild 子名称 不唯一 通过在 tomcat webapps 文件夹中启用和禁用 saml 单点登录来替换现有 war 文件时遇到此问题 我正在使用 apach
  • Java中无参数的for循环

    我在看别人的代码 发现了这段代码 for 我不是 Java 专家 这行代码在做什么 起初 我认为这会创建一个无限循环 但在该程序员使用的同一个类中 while true 其中 如果我错了 请纠正我 是一个无限循环 这两个相同吗 为什么有人会
  • Akka 和 spring 配置

    我正在尝试将 akka 与 spring 结合起来 但没有成功 基本上 我的应用程序似乎不习惯读取 akka 模式 具有架构的 service context xml 的一部分
  • Java .split("|") 不工作

    我刚刚遇到了一个问题分割法 http docs oracle com javase 6 docs api java lang String html split 28java lang String 29for 字符串不适用于字符 作为一个
  • 使用链接列表插入优先级队列的方法

    首先 我觉得我应该提到这是一项作业 我并不是在寻找直接的代码答案 只是为了指出正确的方向 我们被要求在链表中实现优先级队列 我正在努力编写 insert 函数的第一部分 在代码中我尝试检查是否head包含任何内容 如果没有则设置为head
  • 将带有时区的 Joda-Time `DateTime` 转换为没有时区的 DateTime?

    Given a DateTime http www joda org joda time apidocs org joda time DateTime html例如2015 07 09T05 10 00 02 00 using 乔达时间 h
  • 使用 Hibernate 防止无限循环数据检索

    我想知道 想象一个场景 例如 POJO public class User private String userName private String name private String surname private List

随机推荐

  • AMD GPU驱动,ROCM,Pytorch安装教程(A卡6700xt)

    我用的操作系统为ubuntu20 04 其他系统应该类似 xff0c 只是命令稍有不同 安装AMD GPU驱动 AMD驱动下载地址 https www amd com en support kb release notes rn amdgp
  • windows子系统无法启动 wsl命令无效 docker启动报错 解决方法

    windows子系统无法启动wsl命令无效解决方法 xff1a 问题 xff1a wsl l v命令无反应Windows Terminal 不显示 ubuntudocker启动报错 重置网络 按键盘Win 43 R打开 34 运行 34 输
  • gitlab设置中文或者其他语言,gitlab汉化

    前言 xff1a gitlab是我们国内非常好用的git工具 xff0c 但是他默认是英文版本的 xff0c 很多小伙伴可能用不习惯 xff0c 这里分享下他更改为中文版的办法 操作 xff1a 1 英文版本 xff1a 登录后 setti
  • CEF:C++和JS交互

    C 43 43 和JS交互V8原理 xff1a https github com fanfeilong cefutil blob master doc content register v8 extension md CEF一个页面的框架如
  • 计算机应用研究、计算机工程与应用、计算机科学与探索投稿经验

    一 计算机应用研究 xff08 北大核心 xff0c CCF c CCFT3类刊 xff09 总体审稿周期一个多月 xff0c 投过两次算法研究类文章 xff0c 从投稿到正刊录用不超过40天 感觉要求不是很高 xff0c 只有一个外审专家
  • Linux修改系统时间

    一 查看和修改Linux的时区 查看当前时区 命令 xff1a date R 修改设置Linux服务器时区 命令 xff1a tzselect 我这里是使用的CentOS复制相应的时区文件 xff0c 替换系统时区文件 xff1b 或者创建
  • ESP32超详细学习记录:获取B站粉丝数

    获取粉丝数需要知道API调用接口 怎么获得调用的API接口呢 xff1f 以罗翔老师的bilibili账号为例子 本人用的Firefox浏览器 xff0c 其他浏览器操作与Firefox类似 xff0c 在此只是提供获取API的途径 xff
  • ESP32超详细学习记录:NTP同步时间

    本来想从开源项目找找灵感的 xff0c 但是 xff01 那些代码真烂 xff01 xff01 xff01 xff01 xff01 开源项目免不了的就是抄抄抄代码 xff0c 想知道 NTP 是怎么实现的还要读那一堆烂代码 xff0c 烦
  • VS 2015运行报错

    VS 2015运行报错 xff1a L N K 1104 无法打开文件 kernel 32 lib xff0c L N K 1158 xff1a 无法运行 34 r c e x e xff0c L N K 156 必须定义入口点 34 的问
  • fatal: remote error: access denied or repository not exported: /x264

    root 64 ubuntu opt git clone git git videolan org x264 正克隆到 39 x264 39 fatal remote error access denied or repository no
  • 64位ubuntu虚拟机编译后可运行,32位的arm不能运行的原因

    这两天遇到一个怪事 xff0c 就是在64位虚拟机上使用asn1c工具编译生成的asn源文件 xff0c 交叉编译生成静态库 xff0c 在虚拟机上跑的好好的 xff0c 但是在arm上跑的时候无法解码 xff0c 最后查明原因是asn1c
  • 如何使用交叉编译将大量源文件生成动态库

    今天要把算法包生成一个动态库放在ARM上调用运行 xff0c 特此记录一下给大家参考 1 首先将算法文件夹中的源程序生成 o文件 arm span class token operator span linux span class tok
  • 结构体赋值运行时出现段错误(核心已转储)

    今天给嵌套结构体赋值的时候编译没问题 xff0c 但是运行总是段错误 xff0c 后来发现是忘了分配动态内存 xff0c 记得用calloc分配 xff0c 实际结构体嵌套比较复杂 xff0c 在这里举个简单的例子给大家看看 xff0c 引
  • 如何将一段字符串在word中,自动删除换行,两两之间增加空格键

    我们在拉取十六进制数据分析时 xff0c 经常遇到对方只给一条长长的字符串 xff0c 当然 xff0c 我们可以通过代码的方式进行空格添加 xff0c 方便阅读 xff0c 但是有时候身边没有编译工具或者流程复杂 xff0c 介绍一个用w
  • Python学习第5天——洛谷刷题(顺序结构)、循环

    Python学习第5天 1 洛谷刷题1 1 顺序结构1 2 高精度 2 循环2 1 while循环2 2 for循环2 3 range xff08 xff09 函数2 4 用for循环绘图练习2 4 1 绘制同心圆2 4 2 绘制棋盘格 1
  • 如何定义字符串

    如何定义字符串 一维和二维的都可以 xff1b 一维的情况如下 xff1a 1 xff0c char string0 10 2 xff0c char string1 61 34 prison break 34 3 xff0c char st
  • static全局变量与普通的全局变量的区别?

    1 static全局变量与普通的全局变量有什么区别 xff1f static局部变量和普通局部变量有什么区别 xff1f static函数与普通函数有什么区别 xff1f 答 xff1a 全局变量 外部变量 的说明之前再冠以static 就
  • 动态存储方式和静态存储方式

    从变量的作用域的角度来观察 xff0c 变量可以分为全局变量 和局部变量 xff1b 全局变量都是存放在静态存储区中的 因此它们的生存期是固定的 xff0c 存在于程序的整个运行过程 局部变量 xff0c 如果不专门声明存储类别 xff0c
  • c语言中的return 0有什么用?

    return 0是正常退出 xff0c return 非零是异常退出 xff0c 这是返回给控制台的 xff0c 不在编的程序的控制范围内 xff0c 是给操作系统识别的 xff0c 对你的程序无影响 如果是C中 xff0c 定义void
  • JAVA代码实现字符串匹配(一)——BF、KMP

    话不多说 xff0c 直接进入主题 xff1a 题目描述 xff1a 给定两个字符串text和pattern xff0c 请你在text字符串中找出pattern字符串出现的第一个位置 xff08 下标从0开始 xff09 xff0c 如果