java学习日志--Day 2-060720

java字符串比较,很容易使人困惑,在学习过程中,搜集了一些学习资料,供参考。

Java字符串的比较

?

原文网址:http://www.ccw.com.cn/soft/apply/os/htm2005/20051025_1423T.htm

2005-10-21 14:31:50.0

piggy

初学Java的人﹐在学习的过程中遇到的第一个不能理解的问题﹐通常就是字符串的比较问题﹐因为他们实在不能理解明明两个"一样"的字符串﹐为什么有时在做相等的比较运算时会得到果确是false?这个问题在网络上常常都被提出来讨论﹐已经算是一个FAQ到不能FAQ的问题﹐也许是那些初学者们都没有买到一本好的教科书﹐书中并没有教导他们正确的认识字符串﹐要是有认真地读完笔者写的书﹐应该就不会被这个问题所困扰了。好吧!大家都是穷学生买不起五百块以上的书﹐那笔者就借着Java周报的一角﹐详细地说明字符串的比较运算。

在Java程序中﹐你要比较两个对象时﹐首先你要确认要比较的是两件对象的内容?还是比较两个对象变量是否指向同一个对象?如果你是要比较两个对象的内容时﹐请使用该对象所提供的"equals"方法。例如:

String?? str1=newString("hello");
String?? str2=newString("hello");
boolean? b1=str1.equals(str2);
booealn? b2=str2.equals(str1);

上例中用谁的equals方法来使用﹐结果都是一样的(都是true)﹐不过如果str1和str2是两个不同的类别对象﹐意义可就不太一样了﹐这个问题有空再说。如果你比较个对象变量指的是不是同一个对象(比较对象的地址)﹐那你就得使用比较算的"=="运算子。例如:

String str3=newString("hello");
String str4=newString("hello");
String str5=str3;
boolean? b3= str3==str4;
boolean? b4= str3==str5;

b3的结果是false﹐b4的结果是true。不知道笔者这样的解说﹐大家应该可以清楚的知道什么时候要使用equals方法﹐又什么时后要使用"=="运算子。话再说回来﹐会令初学者感到困惑的是类似下面的例子:

String str6="hello";
String str7="hello";
boolean b5=str6.equals(str7);
boolean b6=str6==str7;

b5的结果是true﹐b6的结果也是true。为什么b3的结果会是false﹐而b6的结果却是true呢?上面几个例子中的字符串对象﹐不都是"hello"这个字串吗?你只说对了一半﹐上面例子中str1到str7所指向的字符串对象﹐它们的"内容"都是"hello"这几个字母没错﹐可是它们指向的可不都是同一个字符串对象!str1到str4这四个对象变量指向的字符串对象﹐是经由标准的对象产生方式(new)所产生出来的﹐就算内容一样﹐可是还是不同的物件。就像两个双胞胎来说﹐甚至两个用复制出来的人﹐就算所有的属性都一样﹐两个人还是不同的个体﹐在地球上占用不同的空间。==运算子既然比较的是两个对象变量是否指向的是同一个对象﹐所以b3的结果会是false就是这个原因啰。

那b6的结果为什么会是true呢?这是Java为了效能的考虑﹐而使用的一种技巧。因为字符串对于一个应用程序来说﹐使用上是非常的频繁的﹐如果每个相同内容的字符串对象﹐都占用不同的记忆空间不是很浪费吗?而且JVM也要去处理这些可能只用过一次就不再使用的字符串。因此﹐当你用双引号来产生一个字符串对象时﹐JVM会先到内存中一个名叫StringPool的地方去查询一下﹐是否里面已经有这个字符串了﹐如果有则直接拿出来使用;如果没有就产生一个新的放到里面去。所以str6和str7指向的字串对象﹐都是在StirngPool中的hello字符串对象﹐既然两个对象变量指向的是同一个对象﹐那么用==运算子比较的结果当然就是true了。

被搞的昏头转向了吗?其实就记住笔者最前面所说的﹐当你要比较的是两个对象的内容相不相等时﹐请用equals方法﹐如果要比较的是两个物件变量指向的是不是同一个对象时?请用==运算子。这样一来﹐不管是一般的对象还是字符串对象在比较时﹐你就不会感到困惑了。

?

?

?

?

Java字符串对象的比较

原文网址:http://www.dolc.de/forum/viewthread.php?tid=14421


最近在翻看有关Java Programmer Certificate的辅导资料,看到一些关于
字符串的说明,归纳以下与大家共享。
String s1 = "Hello";
String s2 = "Hello";
if (s1 == s2)
{
System.out.println("s1 = s2");
}
String t1 = new String("Hello");
String t2 = new String("Hello");
if (t1 == t2)
{
System.out.println("t1 = t2");
}

我们知道进行字符串比较需要使用字符串对象String的equals方法。这是
因为操作符 == 进行的是狭义上的比较,而方法equals进行的是广义上的
比较。也就是说,操作符 == 比较的是引用,而方法equals比较的是被引
用的值。用C/C++的术语来说,操作符 == 比较的是指针的值,方法equals
比较的是被指针指向的值。

根据以上认识,我们会认为上述程序片断运行时肯定不会打印s1 = s2,
因为字符串s1和s2是两个不同的String对象,其引用应该是不同的。但是
这样的想法是错误的,因为程序运行时却是打印了s1 = s2。

Java通过重新利用相同的字符串对象从而达到节约系统资源的目的。由于
s1和s2的内容完全相同,因此s1和s2在内存中的地址是完全相同的。正是
因为这样的原因,s1 = s2成立。

那么t1和t2是否象等呢?他们的内容也完全相同。

答案是否定的,因为操作符new强制为t1和t2重新生成新的字符串对象,而
不是重新利用现有的具有相同内容的字符串对象。因此t1和t2在内存中的
地址是不同的,其引用也是不同的。因此,t1 = t2不成立。

小结如下:

1 Java会重复利用现有的字符串对象。
2 操作符new强制生成新的对象。
3 操作符 == 进行狭义上的比较,即引用的比较;方法equals进行广义上
的比较,即被引用的值的比较。

评论1:
我觉得这种现象和JAVA关系不是很大。这是编译技术的原理。怎么说呢,即使对于C++,程序中出现的一般primitive类型常量如i = i+1生成的机器码中包含了的常量硬编码。而如程序中出现的大字符串常量如"liywawei"是专门放在全局数据段(?记不大清了)所有对这个常量的引用都是指向该数据的。所以,我相信JAVA也是如此。而之所以String s1 =new String("liyawei")是创建一个新对象,我认为这是new操作符的语义决定的,就是要建一个新对象,与优化无关,当前的编译器也根本无法优化。下面这段程序:
String s1 =new String("liyawei");
String s2 ="liyawei";
s1="liyawei";
if (s1==s2)
{
System.out.println("==");? ?? ?
}
其结果还是相等。当然,与各位描述的也符合。但我认为是C++/JAVA编译器的普遍方法。而并不是JAVA独有的。

评论2:

你所举的例子是不合适的。主要有下面几点:

1 字符串s1创建在先,内容为"liyawei"的字符串在内存中已经存在以后再创建字符串s2。由于在创建s2的时候没有使用new操作符,字符串s2实际上和s1的物理地址是一样的,因此s2和s1的引用也是相同的。
2 在你的第三行代码中重新定义了s1。由于字符串对象String的不可更改性(也?就是immutable属性,FG解释一下这个中文术语吧),这个操作实际上不是更改?s1的内容,而是重新创建一个名为s1的字符串,其内容为"liyawei"。根据1,?内容为"liyawei"的字符串再重新创建s1之前已经存在,因此s1重新使用这个?已经存在的字符串,所以新的s1和s2的物理地址是一样的,因此s1和s2的引用也是相同的。
3 在Java中字符串对象String不属于基本数据类型。
4 在C/C++的编译中,有的编译器有可能对你的程序进行优化,譬如说将程序中大量出现的常量专门放到指定数据并且可以重用,但是你必须在程序中显式的声明这些数据是常量。另外,即使你已经显式的声明这些数据是常量,是否进行这些优化还取决于你的编译器和优化参数。而在Java中,由于字符串对象的不可更改性,所有的字符串对象都被认为是常量,这种优化是强制性的。

以上所述,有什么不正确的地方,请多多指教。

评论3:
为什么会在字符串上大家发生认识模糊,就是qyjohn提到的
java和c中对string类型的不同构造,在c中它是基本数据类型,
而在Java众它是一个类。当然具体到编译那一层,大家都差不多。

JAVA中文比较问题的分析解决

bill 发表于2005-04-11 作者:yousp
原文来自http://www.matrix.org.cn/resource/article/1/1428.html

?

Java的中文问题由来已久,前不久笔者需要做内存中的中文比较排序,对字符串进行GBK或者GB2312编码以后,使用String.compareTo方法仍然不能得到正确结果。因此,怀着怀疑的态度,对JDK中String类的源代码做了一翻探究。(作者使用JDK为1.3.1版本)
以下是String.java中compareTo的源代码,请注意其中的注释:

public class String 
{
?…
? ? public int compareTo(String anotherString) {
? ? ? ?int len1 = count;
? ? ? ?int len2 = anotherString.count;
? ? ? ?//n为两个字符串长度的最小者
? ? ? ?int n = Math.min(len1, len2);
? ? ? ?//获取字符数组
? ? ? ?char v1[] = value;
? ? ? ?char v2[] = anotherString.value;
? ? ? ?//取偏依位置
? ? /** The offset is the first index of the storage that is used. */
? ? ? ?//offset 是第一个存储索引
? ? ? ?int i = offset;
? ? ? ?int j = anotherString.offset;
? ? ? ?//如果i == j
? ? ? ?//这里可能是判断取同一内存中两个字符串的情景。。。
? ? ? ?// A? ?<--? ?<----
? ? ? ?// B? ? s1? ? ? |
? ? ? ?// C? ?<--? ? ? |
? ? ? ?// D? ? ? ? ? ? s2
? ? ? ?// E? ? ? ? ? ? |
? ? ? ?// F? ? ? ? ? ? |
? ? ? ?// G? ?<----------
? ? ? ?// 可能这种情况 i = j
? ? ? ?if (i == j) {
? ? ? ? ? ?int k = i;
? ? ? ? ? ?int lim = n + i;
? ? ? ? ? ?while (k < lim)
? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ?char c1 = v1[k];
? ? ? ? ? ? ? ? ? ? ?char c2 = v2[k];
? ? ? ? ? ? ? ? ? ? ?if (c1 != c2) file://直到找到一个不相等的字符,返回c1 - c2
? ? ? ? ? ? ? ? ? ? ? ? ? ? return c1 - c2;
? ? ? ? ? ? ? ? ? ? ?k++;
? ? ? ? ? ?}
? ? ? ?} else {
? ? ? ? ? ?while (n-- != 0) file://直到两个字符串长度记数为0
? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ?char c1 = v1[i++]; file://分别取字符
? ? ? ? ? ? ? ? ? ? ?char c2 = v2[j++];
? ? ? ? ? ? ? ? ? ? ?if (c1 != c2) {? //发现不相等,立即返回c1 - c2;
? ? ? ? ? ? ? ? ? ? ? ? ? ? return c1 - c2;
? ? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ?}
? ? ? ?}
? ? ? ?return len1 - len2;
//最后这里可能出现的情况是: 两个字符串比较完之后还没有得到结果。相等的情况
? ? }

}//end of class String
为什么Java在做汉字的CompareTo时比较会有问题呢?通过对compareTo源代码的分析发现,关键在于JDK的compareTo实现是直接使用Char来进行比较的:
char c1 = v1[k];
char c2 = v2[k];
可是当Java使用GB2312编码时,一个对汉字所获取到的Char值却是不规则的,即一个汉字在Java中作为一个char来处理(双字节字符)时,将这样的双字节字符进行强制转换成int类型时,所得到的不是包含了汉字编码顺序的中文内码。可以看一下一组测试数据可以看到其中奥妙:

字符

Char值

Byte[]值

按Byte[]合成的值

25105

[50:46]

[-5046]

29233

[80:82]

[-8082]

21271

[79:79]

[-7979]

20140

[66:87]

[-6687]

22825

[52:20]

[-5220]

23433

[80:78]

[-8078]

38376

[61:59]

[-6159]

A

65

[-65]

[65]

B

66

[-66]

[66]

C

67

[-67]

[67]

D

68

[-68]

[68]

?

?

?

?


按照中文顺序:“我”字应该在“爱”字后面,因此理论上来讲"我"字的Char值应该比“爱"字的char值要大。可是不知道为什么Java的汉字char(两个byte)->int类型的转换会发生很大偏差。而失去了汉字原本在GBK规范当中,按内码排列好的顺序。但从一个汉字拆分成2个字节的byte[]时,所得到的值并没有打乱GBK编码规定的顺序,因此得到解决问题的思路:将String进行GB2312编码后取得某个汉字获取其Char值时,将汉字拆分成2个字节byte[]再进行计算,从而得到正确的内码。

因此我自己写了下面这样几个函数,基本上解决了汉字比较的问题:

函数包括三个,你可以随意放置到任何类当中作为辅助函数使用(Private Helper)。

n public int compare(String s1, String s2) :主要工作是为比较做一些前期的编码工作可以说是系统的一个外壳。
n public int chineseCompareTo(String s1, String s2):该函数则是中文字符串比较主体,其内部实现了比较的最基本逻辑,和JDK的compareTo所使用的逻辑是一样的。调用接口也一样。

n public static int getCharCode(String s):该函数则负责将一个以字符串形式存在的字符转换成为int编码,儿不损失其位置信息。注意输入通常是:“我”或者“A”,如果输入更长的字符串,则改函数获得的是第一个字符的值。


private static String __ENCODE__ = "GBK"; file://一定要是GBK
private static String __SERVER_ENCODE__ = "GB2312"; file://服务器上的缺省编码
/*
比较两字符串
*/
? ? ? ?public int compare(String s1, String s2)
? ? ? ?{String m_s1 = null, m_s2 = null;
? ? ? ? ? ? ? try
? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ?//先将两字符串编码成GBK
? ? ? ? ? ? ? ? ? ? ?m_s1 = new String ( s1.getBytes(__SERVER_ENCODE__), __ENCODE__);
? ? ? ? ? ? ? ? ? ? ?m_s2 = new String ( s2.getBytes(__SERVER_ENCODE__), __ENCODE__);
? ? ? ? ? ? ? }
? ? ? ? ? ? ? catch( Exception ex)
? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ?return s1.compareTo(s2);
? ? ? ? ? ? ? }
? ? ? ? ? ? ? int res = chineseCompareTo(m_s1, m_s2);
? ? ? ? ? ? ? System.out.println("比较:" + s1 + " | " + s2 + "==== Result: " + res);
? ? ? ? ? ? ? return res;
? ? ? ?}
//获取一个汉字/字母的Char值
? ? ? ?public static int getCharCode(String s)
? ? ? ?{
? ? ? ? ? ? ? if (s==null && s.equals(“”)) return -1; file://保护代码
??????????????? byte [] b = s.getBytes();
? ? ? ? ? ? ? int value = 0;
? ? ? ? ? ? ? //保证取第一个字符(汉字或者英文)
? ? ? ? ? ? ? for (int i = 0; i < b.length && i <= 2; i ++)
? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ?value = value * 100 + b[i];
? ? ? ? ? ? ? }
? ? ? ? ? ? ? return value;
? ? ? ?}
????? ?//比较两个字符串
? ? ? ?public int chineseCompareTo(String s1, String s2)
? ? ? ?{
? ? ? ? ? ? ? int len1 = s1.length();
? ? ? ? ? ? ? int len2 = s2.length();
? ? ? ? ? ? ? int n = Math.min(len1, len2);
? ? ? ? ? ? ? for (int i = 0; i < n; i ++)
? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ?int s1_code = getCharCode(s1.charAt(i) + "");
? ? ? ? ? ? ? ? ? ? ?int s2_code = getCharCode(s2.charAt(i) + "");
? ? ? ? ? ? ? ? ? ? ?if (s1_code != s2_code) return s1_code - s2_code;
? ? ? ? ? ? ? }
? ? ? ? ? ? ? return len1 - len2;
? ? ? ?}


可见,对系统源代码的解剖,能让我们在迷惑之余同样有机会窥探系统内部运作的奥妙。不过让人非常费解的是,Java内部的某些类书写风格非常不好,同时存在一些Bug。不过这也许是笔者个人感受。偶有所获,愿与大家共同分享,其中疏漏之处望不吝赐教。

bill-转自:csdn

?

评论:

Colator好像排出来的也有错误吧! "自由"和“芙蓉''和“奥运”就不行。

#3705 评论作者:wym 发表时间:2005-07-19 04:40

Colator好像排出来的也有错误吧! "自由"和“芙蓉''和“奥运”就不行。

评论作者:DanielYWoo 发表时间:2005-05-09 04:02

呵呵,太麻烦了,你根本不用这样排序,Java的类库有Colator,专门用来处理不同字符集排序的,直接用就好了。

?

?

java字符串比较和替换的问题


S1="AAABBB"
S2="AAA"
如何取出"BBB"
比较str1中是否包含str2,如果包含就打印出除str2之外的?str1的其余内容阿?
我自己写了一个实现这个功能的程序?
public?class?StrCom?
{

static?String?s1?=?"AAABBB";
static?String?s2?=?"AAA";

public?StrCom()?
{
}

public?void?StrMak(String?str1,String?str2)
{
int?a?=?str1.indexOf(str2);
if?(a?!=?-1)
{
int?b??=?str2.length();
String?res?;
if(a?==?0)
{
res?=?str1.substring(b);
}
else
{
res?=?str1.substring(0,a)?+?str1.substring(a+b);
}
System.out.println(res);
}
else
{
System.out.print("str1?doesn't?have?str2");
}
}

public?static?void?main?(String?args[])
{
StrCom?test?=?new?StrCom();
test.StrMak(s1,s2);
}
}


s1.replaceFirst(s2,"");

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值