作为建议,即使这不是对您的问题的“完整答案”,比较字符串通常也是一项“繁重”操作(即使您首先检查它们的长度,事实上,这是第一个equals() 方法在比较字符串时已经执行的操作)
我的建议是下一步:创建一个dummy hashcode()-like 方法。它不是真正的hashcode(),而是与您的代码读取该单词的顺序相关联的数字。类似于加密方法,但要简单得多。
请注意,string.hashCode() 不起作用,因为第一个文档中的单词“Hello”与第二个文档中的单词“Hello”返回的哈希码不同。
数据“变暖” - PreConversion
假设您有一个共享的HashMap<String,Integer> (myMap),其中键是字符串,值是整数。 请注意,HashMap 在 java 中使用少于 10 个字符(通常是英文)的字符串键的散列非常快。无需任何检查,只需将每个单词与其计数器值放在一起:
myMap.put(yourString, ++counter);
假设您有 2 个文档:
1.txt- Welcome mate what are you doing here
2.txt- Mate I was here before are you dumb
我假设您已经将所有单词小写,并删除了重复项。
您开始阅读第一个文档并将每个单词分配给一个数字。地图如下所示:
KEY VALUE
welcome 1
mate 2
what 3
are 4
you 5
doing 6
here 7
现在是第二个文档。如果一个键被重复,put() 方法将更新它的值。所以:
KEY VALUE
welcome 1
mate 8
what 3
are 13
you 14
doing 6
here 11
I 9
was 10
before 12
dumb 15
一旦完成,您将创建另一个 HashMap (reverseMap),以相反的方式:
KEY VALUE
1 welcome
8 mate
3 what
13 are
14 you
6 doing
11 here
9 I
10 was
12 before
15 dumb
您将两个文档都转换为整数列表,因此它们看起来像:
1.txt- Welcome mate what are you doing here
2.txt- Mate I was here before are you dumb
到:
listOne - [1, 8, 3, 13, 14, 6, 11]
listTwo - [8, 9, 10, 11, 12, 13, 14, 15]
重复的单词、位置和序列
要在两个文档中查找重复项:
listDuplicates.retainAll(listOne);
结果是:
listDuplicates- [8,11,13,14]
因此,在 2 个文档中找到的总共 listOne.size()+listTwo.size() = 15 个单词中,4 个是重复的,11 个是唯一的。
为了得到转换后的值,只需调用:
for (Integer i : listDuplicates)
System.out.println(reverseMap.get(i)); // mate , here, are, you
现在识别出重复项,listOne 和 listTwo 现在也可以用于:
- 识别每个列表中的位置(这样我们就可以得到这些词的位置差异)。第一个单词的值为 -1,因为它是第一个单词,并且与前一个单词没有差异,但并不一定意味着它们与其他单词有任何区别(它们只是第一个重复项)。
如果下一个元素的值为-1,这意味着[8]和[11]也是连续的:
doc1 doc2 difDoc1 difDoc2
[8] 2 1 -1 (0-1) -1 (0-1)
[11] 7 4 -5 (2-7) -3 (1-4)
[13] 4 6 3 (7-4) -2 (4-6)
[14] 5 7 -1 (4-5) -1 (6-7)
在这种情况下,[14] 中显示的距离与其之前的副本 ([13] 和 [14] 之间的差异) 在两个文档中是相同的:-1:这意味着不仅是重复的,而且两者都被放置在两个文档中。
因此,我们不仅发现了重复的单词,而且还发现了这些行之间两个单词的重复序列:
[13][14]--are you
相同的机制(确定两个文档中相同变量的 diff 为 -1)也有助于找到 2 个或更多单词的完整重复序列。如果所有重复项在两个文档中都显示出 -1 的差异,这意味着我们找到了完整的重复行:
在本例中显示得更清楚:
doc1- "here i am" [4,5,6]
doc2- "here i am" [4,5,6]
listDuplicates - [4,5,6]
doc1 doc2 difDoc1 difDoc2
[4] 1 1 -1 (0-1) -1 (0-1)
[5] 2 2 -1 (1-2) -1 (1-2)
[6] 3 3 -1 (2-3) -1 (2-3)
两个文档中相同变量的所有差异都是 -1 -> 两个文档中所有重复项彼此相邻 --> 两个文档中的句子完全相同。所以,这一次,我们发现了一个完整的重复行,包含 3 个单词。
[4][5][6] -- here i am
除了这个重复序列搜索之外,这个差异表在计算重复单词的方差、中位数、...时也会很有帮助,以便获得某种“相似性”因子 (类似于文件之间公平的基本指示价值。绝不是确定的,但在某种程度上是有帮助的)
独特的价值 - 有助于获得非股权指示?
将使用类似的机制来获取这些唯一值。例如,通过从reverseMap 中删除重复项:
for (Integer i: listDuplicates)
reverseMap.remove(i);
现在 reverseMap 只包含唯一值。 reverseMap.size() = 11
KEY VALUE
1 welcome
3 what
6 doing
9 I
10 was
12 before
15 dumb
为了得到唯一的词:
reverseMap.values() = {welcome,what,doing,I,was,before,dumb}
如果您需要知道哪些唯一词来自哪个文档,您可以使用reverseMap(因为在您对它们执行诸如retainAll之类的方法后,列表可能会被更改):
- 计算第一个文档的字数。这次是 7。
- 如果 reverseMap 的键是 {welcome,what,doing}
- 如果关键字 >7,则该唯一词来自第二个文档。
{I,was,before,dumb}
唯一性因素也可能是另一个指示性因素,这样,一个负面因素(因为我们在这里寻找相似之处)。还是很有帮助的。
equals 和 hashCode - 避免
由于字符串的hashcode() 方法不会为两个相同的单词返回相同的值(仅适用于两个相同的字符串对象引用),因此在这里不起作用。 String.equals() 方法通过比较字符(也检查长度,就像你手动做的那样)来工作,如果用于大文档,这将是完全矫枉过正:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String) anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
我的意见是尽量避免这种情况,特别是 hashCode() 永远不要使用,因为:
String one = "hello";
String two = "hello";
one.hashCode() != two.hashCode()
这有一个例外,但只有在编译器实习字符串时;一旦加载了数千个,编译器将不再使用它们。在极少数情况下,两个字符串对象都引用相同的缓存内存地址,这也是正确的:
one.hashCode() == two.hashCode() --> true
one == two --> true
但这些确实是不寻常的例外,一旦字符串实习不踢,那些hashCodes 将不相等,并且比较字符串的运算符== 将返回false,即使字符串持有相同的值(和往常一样,因为它可以比较它们的内存地址)。