长字符串中检测短字符串的出现次数(四)

本文深入探讨了字符串处理中的关键方法,包括contains()和indexOf()的使用,以及如何利用这些方法检测子字符串在长字符串中的出现次数。通过具体示例,文章详细解析了indexOf()及其变体的实现原理,为读者提供了理解和运用字符串操作的强大工具。

东家蝴蝶西家飞,白骑少年今日归。 愿,所有迷茫的人,都不再迷茫的,愿,所有努力工作的人,都可以得到应有的回报,愿,所有的人,都可以找到回家的方向,愿,再无苦痛,再无离别。

上一章简单介绍了字符串数组的排序(三),如果没有看过,请观看上一章

一. 题目的解释

有一个长字符串,如"abcdabbababbababbab",检测子字符串"ab" 在这个长字符串中出现的次数。
判断一个字符串里面是否包含,或者含有另外一个字符串,可以用contains() 方法和indexOf() 方法,但contains() 方法,只返回一个boolean 的状态值,不能记录出现的位置,所以可以用indexOf() 来进行相应的判断。

二. contains()方法和indexOf() 方法

其中,contains() 方法是:

public boolean contains(CharSequence s) {
	//内部调用的是indexOf()的方法。
        return indexOf(s.toString()) > -1;
    }

其中,indexOf() 方法重载了多种形式,也有一个变体, lastIndexOf()
在这里插入图片描述
其中,主要有四种变体,一种是从头开始查询,即indexOf(), 另外一种是从尾开始查询,即lastIndexOf(), 一种传入的是 int 类型,另外一种传入的是 String 字符串形式。

二.一 indexOf(int) 形式

如果没有开始位置 fromIndex :

public int indexOf(int ch) {
		// 会默认从0 开始查询,调用有开始位置的方法
        return indexOf(ch, 0);
    }

有开始位置的方法:

 public int indexOf(int ch, int fromIndex) {
 		// 取当前字符串的最大长度
        final int max = value.length;
        // 如果传入的开始位置< 0,即为0 或者是为负数,那么就处理成0
        if (fromIndex < 0) {
            fromIndex = 0;
        } else if (fromIndex >= max) { // 如果大于等于最大的长度,就返回-1. formIndex的范围是0~max-1
            // Note: fromIndex might be near -1>>>1.
            return -1;
        }
	// 判断,如果是普通的数字,小于 65536,unicode 的编码是两个字节,最大为65536
        if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
            // handle most cases here (ch is a BMP code point or a
            // negative value (invalid code point))
            //拆分成字符数组
            final char[] value = this.value;
            for (int i = fromIndex; i < max; i++) {
            	// 如果相同,返回 i的索引值。
                if (value[i] == ch) {
                    return i;
                }
            }
            return -1;
        } else {
        // 进行特殊值的查询,即>65536的时候的方法
            return indexOfSupplementary(ch, fromIndex);
        }
    }
 private int indexOfSupplementary(int ch, int fromIndex) {
 		// 小于字符的最大值, 0X10FFFF 这个数时,还能比较
        if (Character.isValidCodePoint(ch)) {
        //字符数组
            final char[] value = this.value;
            // 高代码
            final char hi = Character.highSurrogate(ch);
            final char lo = Character.lowSurrogate(ch);
            //该对象字符串的长度
            final int max = value.length - 1;
            for (int i = fromIndex; i < max; i++) {
            	//相同,就返回i , 这个相同是什么意思,不知道,猜想是大数的一种处理形式。 没有找到,返回-1
                if (value[i] == hi && value[i + 1] == lo) {
                    return i;
                }
            }
        }
        //如果大于 0X10FFFF, 则不能比较了,直接返回-1
        return -1;
    }
public static char highSurrogate(int codePoint) {
        return (char) ((codePoint >>> 10)
        	//\ud800    0x10000即65536
            + (MIN_HIGH_SURROGATE - (MIN_SUPPLEMENTARY_CODE_POINT >>> 10)));
    }
public static char lowSurrogate(int codePoint) {
	// \uDC00
        return (char) ((codePoint & 0x3ff) + MIN_LOW_SURROGATE);
    }

二.二 lastIndexOf(int)

没有开始位置的,会设置默认的位置为最后一个。调用有开始位置的方法.

	public int lastIndexOf(int ch) {
        return lastIndexOf(ch, value.length - 1);
    }

有开始位置的方法: 其中,返回的值 只能 <=formIndex的值

  public int lastIndexOf(int ch, int fromIndex) {
  // 65536
        if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
            // handle most cases here (ch is a BMP code point or a
            // negative value (invalid code point))
            final char[] value = this.value;
            //最小值, 如果formIndex 传入的值> 最大值,那么就为最后的位置。 如果<最大值,那么就是当前的formIndex的位置。  判断formIndex <0 的情况, 返回的是-1
            int i = Math.min(fromIndex, value.length - 1);
            for (; i >= 0; i--) {
                if (value[i] == ch) {  //如果相同, 返回i,不相同返回-1
                    return i;
                }
            }
            return -1;
        } else {
        // >65536时
            return lastIndexOfSupplementary(ch, fromIndex);
        }
    }
private int lastIndexOfSupplementary(int ch, int fromIndex) {
		// 与indexOf(int) 一样,都是不懂。
        if (Character.isValidCodePoint(ch)) {
            final char[] value = this.value;
            char hi = Character.highSurrogate(ch);
            char lo = Character.lowSurrogate(ch);
            // 求最小的值,此时判断为length-2
            int i = Math.min(fromIndex, value.length - 2);
            for (; i >= 0; i--) {
                if (value[i] == hi && value[i + 1] == lo) {
                    return i;
                }
            }
        }
        return -1;
    }

二.三 测试 indexOf(int) 和lastIndexOf(int)

@Test
	public void testA(){
		System.out.println("abcd".indexOf(98)); //1  98的unicode码是b
		System.out.println("abcd".indexOf(98,2)); // -1
		System.out.println("abcd".lastIndexOf(98)); //1
		System.out.println("abcd".lastIndexOf(98,2)); //返回的结果只能是小于fromIndex
	}

二.四 indexOf(String)

没有传入开始位置的,默认开始位置为0

 public int indexOf(String str) {
        return indexOf(str, 0);
    }

有参数位置的,将当前字符串,开始位置,验证的长度,目标字符串,目标的位置,
目标的长度,还有起始位置传入进去:

 public int indexOf(String str, int fromIndex) {
        return indexOf(value, 0, value.length,
                str.value, 0, str.value.length, fromIndex);
    }

这个目标的方法,不太好解释,也不容易理解,可以举例子进行相应的说明。
如: 传入参数为:

indexOf("abcdefghijklmn".toCharArray(),
						1, 10,"cdefg".toCharArray(),1,2, 1)

	// 传入的参数是 abcdefghijklmn 1 10 cdefg 1 2 1
	public static int indexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset,
			int targetCount, int fromIndex) {
		if (fromIndex >= sourceCount) { // 比截取后的源字符串长, 如果目标字符串的长度==0,
			// 返回源字符串的数目,否则返回-1. 即比较的是""是返回sourceCount,否则是-1
			return (targetCount == 0 ? sourceCount : -1);
		}
		if (fromIndex < 0) { // 为负数时,处理成0的情况。
			fromIndex = 0;
		}
		if (targetCount == 0) { // 如果目标的长度是0,即比较的是"" 时,返回fromIndex
			return fromIndex;
		}
		// 为target[1]=d
		char first = target[targetOffset]; // 目标字符串最开始的字符
		// 最大是:1+(10-2)=9 ,也就是比较的时候,并不是比较完,只比较到1+10=11 -2=9,即-目标数目处。
		int max = sourceOffset + (sourceCount - targetCount);
		// 从源字符串的起始位置+formIndex处开始,即从1+1=2 c处开始。 为9
		for (int i = sourceOffset + fromIndex; i <= max; i++) {
			/* Look for first character. */
			if (source[i] != first) { // 现在当前位置为c,不为d,还差一点,需要先移动
				// 到d 处。 即 令i++, 但要保证i<=max 并且 不相同。 如果相同,就 ; 跳出了循环。
				while (++i <= max && source[i] != first)
					;
			}
			// 现在i 已经到达了d 处,即i=3
			/* Found first character, now look at the rest of v2 */
			if (i <= max) { // <=9时,
				int j = i + 1; // j=4 时, e处
				// 4+2-1=5 结束的点。 f处。
				int end = j + targetCount - 1;
				// 从1+1=2 处开始,从c 开始, 到f处为止并且 资源[j]==目标的k 时,j++,k++
				for (int k = targetOffset + 1; j < end && source[j] == target[k]; j++, k++)
					;
				/**
				 * k=1+1=2 ,c处。 j=4 < 5, 并且 source[4]=e == target[2]=e ==> j=5
				 * k=3 j=5<5 不符合条件,退出循环。
				 */
				// 其中 j=5=5, 相同,找到了。
				if (j == end) {
					/* Found whole string. */
					// 返回3-1=2 处。
					return i - sourceOffset;
				}
			}
		}
		// 否则,即j !==end 时,返回-1
		return -1;
	}

其实,这个方法表明的主要意思是什么呢,可以简单理解成:

// 传入的参数是 abcdefghijklmn 1 10 cdefg 1 2 1
	public static int indexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset,
			int targetCount, int fromIndex) {
		//1.将 源字符数组封装成String 字符串
		String sourceString=new String(source);
		//2. 将源字符串进行相应的拆分,找到真正的源数据。
		sourceString=sourceString.substring(sourceOffset,sourceOffset+sourceCount);
		System.out.println("sourceString:"+sourceString); //bcdefghijk
		//3 将目标字符数组封装成String 字符串
		String targetString=new String(target);
		//4 将目标字符串进行相应的拆分,找到真正的源数据。
		targetString=targetString.substring(targetOffset,targetOffset+targetCount);
		System.out.println("targetString:"+targetString); //de
		//5. 实际上就变成了 sourceString 与targetString 在fromIndex 处的比较
		return sourceString.indexOf(targetString,fromIndex);
	}

二.五 lastIndexOf()

public int lastIndexOf(String str) {
		// 默认从最大长度处开始
        return lastIndexOf(str, value.length);
    }
public int lastIndexOf(String str, int fromIndex) {
		//转换成数组
        return lastIndexOf(value, 0, value.length,
                str.value, 0, str.value.length, fromIndex);
    }
 static int lastIndexOf(char[] source, int sourceOffset, int sourceCount,
            char[] target, int targetOffset, int targetCount,
            int fromIndex) {
        /*
         * Check arguments; return immediately where possible. For
         * consistency, don't check for null str.
         */
        int rightIndex = sourceCount - targetCount;
        if (fromIndex < 0) {
            return -1;
        }
        if (fromIndex > rightIndex) {
            fromIndex = rightIndex;
        }
        /* Empty string always matches. */
        if (targetCount == 0) {
            return fromIndex;
        }

        int strLastIndex = targetOffset + targetCount - 1;
        char strLastChar = target[strLastIndex];
        int min = sourceOffset + targetCount - 1;
        int i = min + fromIndex;

    startSearchForLastChar:
        while (true) {
            while (i >= min && source[i] != strLastChar) {
                i--;
            }
            if (i < min) {
                return -1;
            }
            int j = i - 1;
            int start = j - (targetCount - 1);
            int k = strLastIndex - 1;

            while (j > start) {
                if (source[j--] != target[k--]) {
                    i--;
                    continue startSearchForLastChar;
                }
            }
            return start - sourceOffset + 1;
        }
    }

与indexOf() 一样,可以简单理解成:

// 传入的参数是 abcdefghijklmn 1 10 cdefg 1 2 10  返回结果为2
	public static int lastIndexOf(char[] source, int sourceOffset, int sourceCount, char[] target, int targetOffset,
			int targetCount, int fromIndex) {
		//1.将 源字符数组封装成String 字符串
		String sourceString=new String(source);
		//2. 将源字符串进行相应的拆分,找到真正的源数据。
		sourceString=sourceString.substring(sourceOffset,sourceOffset+sourceCount);
		System.out.println("sourceString:"+sourceString); //bcdefghijk
		//3 将目标字符数组封装成String 字符串
		String targetString=new String(target);
		//4 将目标字符串进行相应的拆分,找到真正的源数据。
		targetString=targetString.substring(targetOffset,targetOffset+targetCount);
		System.out.println("targetString:"+targetString); //de
		//5. 实际上就变成了 sourceString 与targetString 在fromIndex 处的比较
		return sourceString.lastIndexOf(targetString,fromIndex);
	}

二.六 测试

@Test
	public void test2(){
		System.out.println("abcdef".indexOf("cd")); //2 
		System.out.println("abcdef".indexOf("cd",4)); //-1 
		System.out.println("abcdef".indexOf("cd",10)); //-1
		System.out.println("abcdef".indexOf("cd",-2)); //2 会将-2处理成0
		System.out.println("abcdef".lastIndexOf("cd")); //2
		System.out.println("abcdef".lastIndexOf("cd",4)); //2
		System.out.println("abcdef".lastIndexOf("cd",10)); //2
		System.out.println("abcdef".lastIndexOf("cd",-2)); //-1  会将-2处理成0
		// 对于传入字符数组的源代码形式,并不能进行访问
	}

三. 第一种方式 对字符串进行截取处理

利用indexOf(string) 方法

	// 传入参数为 abcdabbababbababbab  ab
	public static int getShowCount1(String showStr,String subString){
		StringBuilder sbBuilder=new StringBuilder(showStr);
		//要看是否包含。 可以用contain和indexOf来判断。 但不需要知道出现的位置,方便下一次的搜索。
		//用indexOf. 不知道具体多少次,用while. 当为-1时,表示没有了。
		int count=0;
		int index=0;
		// 有值的情况下
		while((index=sbBuilder.indexOf(subString))!=-1){
			//重新进行下一次的循环。 对长字符进行相关的验证,将前端已经出现的去除掉。
			// 这个时候,showStr 第一次循环变成了  cdabbababbababbab  ab 继续传入   count++
			// 第二次循环时,变成了bababbababbab ab count++
			// 第三次循环时, 变成了abbababbab ab count++   依次继续,会源数据进行相应的操作
			sbBuilder.substring(index+subString.length());
			count++;
		}
		return count;
	}

四.第二种方式, 利用indexOf(Strng,fromIndex) 方法

// 传入参数为 abcdabbababbababbab  ab
	public static int getShowCount2(String showStr,String subString){
		//要看是否包含。 可以用contain和indexOf来判断。 但contains不需要知道出现的位置,方便下一次的搜索。
		//用indexOf. 不知道具体多少次,用while. 当为-1时,表示没有了。
		int count=0;
		int sublength=subString.length();
		int index=-sublength;  //从哪个开始。 自然是从0开始,将index的初始值默认为-sublength
		while((index=showStr.indexOf(subString,index+sublength))!=-1){
			// 不断的改变fromIndex的值
			count++;
		}
		return count;
	}

结果为7

谢谢!!!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

两个蝴蝶飞

你的鼓励,是老蝴蝶更努力写作的

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值