如何解决在一组元素中找到一个复杂元素
我有一个函数,可以让我在一组不完整的元素和至少一个元素之间找到匹配项。 22.2.X.13 是一个不完整元素的示例,其中有一个项目(用X定义)可以采用任何值。
此功能的目标是在一组元素中找到至少一个元素,该元素的第一个位置为22,第二个为2,第四个为13。
例如,如果我们考虑集合:
{
20.8.31.13,32.3.29.13,24.2.12.13,19.2.37.13,22.2.22.13,27.17.22.13,26.22.32.13,22.3.22.13,20.19.12.13,17.4.37.13,31.8.34.13
}
该函数的输出返回 True ,因为存在与22.2.22.13
相对应的元素22.2.X.13
。
我的函数将每对元素(例如字符串)和元素的每一项都比较为整数:
public boolean containsElement(String element) {
StringTokenizer strow = null,st = null;
boolean check = true;
String nextrow = "",next = "";
for(String row : setofElements) {
strow = new StringTokenizer(row,".");
st = new StringTokenizer(element,".");
check = true;
while(st.hasMoretokens()) {
next = st.nextToken();
if(!strow.hasMoretokens()) {
break;
}
nextrow = strow.nextToken();
if(next.compareto("X") != 0) {
int x = Integer.parseInt(next);
int y = Integer.parseInt(nextrow);
if(x != y) {
check = false;
break;
}
}
}
if(check) return true;
}
return false;
但是,这是一个昂贵的操作,尤其是当字符串的大小增加时。您能建议我另一种策略或数据结构来快速执行此操作吗?
我的解决方案与字符串紧密相关。但是,我们可以考虑其他类型的元素(例如数组,列表,树节点等)
感谢大家的回答。我已经尝试了几乎所有功能,以及试验台:
myFunction: 0ms
hasMatch: 2ms
Stream API: 5ms
isIPMatch; 2ms
我认为正则表达式的主要问题是创建模式和匹配字符串的时间。
解决方法
您要使用正则表达式,正则表达式正是针对此类任务而设计的。签出demo。
22\.2\.\d+\.13
Java 8及更高版本
您可以使用Java 8或更高版本的Stream API,使用Pattern
和Matcher
类找到至少一个与正则表达式匹配的内容:
Set<String> set = ... // the set of Strings (can be any collection)
Pattern pattern = Pattern.compile("22\\.2\\.\\d+\\.13"); // compiled Pattern
boolean matches = set.stream() // Stream<String>
.map(pattern::matcher) // Stream<Matcher>
.anyMatch(Matcher::matches); // true if at least one matches
Java 7及更低版本
方法与Stream API相同:如果发现匹配项,则使用break
语句进行短路的for-each循环。
boolean matches = false;
Pattern pattern = Pattern.compile("22\\.2\\.\\d+\\.13");
for (String str: set) {
Matcher matcher = pattern.matcher(str);
if (matcher.matches()) {
matches = true;
break;
}
}
,
您可以按照Nikolas Charalambidis(+1)的建议,通过基于正则表达式的方式解决问题,也可以采取不同的方法。为了避免重复使用其他答案,在这里我将重点介绍使用split方法的另一种方法。
public boolean isIPMatch(String pattern[],String input[]) {
if ((pattern == null) || (input == null) || (pattern.length <> input.length)) return false; //edge cases
for (int index = 0; index < pattern.length; index++) {
if ((!pattern[index].equals("X")) && (!pattern[index].equals(input[index]))) return false; //difference
}
return true; //everything matched
}
在通过String
将项目转换为与split
数组进行比较之后,您可以在循环中调用上述方法。
对于字符串,正则表达式可以更好地解决任务:
private boolean hasMatch(String[] haystack,String partial) {
String patternString = partial.replace("X","[0-9]+").replace(".","\\.");
// "22.2.X.13" becomes "22\\.2\\.[0-9]+\\.13"
Pattern p = Pattern.compile(patternString);
for (String s : haystack) {
if (p.matcher(s).matches()) return true;
}
return false;
}
对于其他类型的对象,这取决于它们的结构。
- 如果有某种顺序,则可以考虑使元素实现
Comparable
-然后可以将它们放入TreeSet
(或作为TreeMap
中的键),它将始终保持排序。这样,您就只能与可以匹配的元素进行比较:mySortedSet.subSet(fromElement,toElement)
仅返回这两个元素之间的元素。 - 如果没有顺序,则只需将所有元素与“模式”进行比较。
请注意,字符串是可比较的,但是它们的默认排序顺序会忽略.
-分隔符的特殊语义。因此,您可以谨慎地实施基于树集的方法,以使搜索优于线性搜索。
其他答案已经讨论过使用正则表达式通过转换例如22.2.X.13
至22\.2\.\d+\.13
(不要忘记也逃脱.
或它们的意思是“任何”)。但是,尽管这肯定会更简单,也可能会更快一些,但它并不会降低整体复杂性。您仍然必须检查集合中的每个元素。
相反,您可以尝试以以下形式将一组IP转换为嵌套的Map
:
{20: {8: {31: {13: null}},19: {12: {13: null}}},22: {2: {...},3: {...}},...}
(当然,您应该只创建一次此结构,而不是为每个搜索查询创建该结构。)
然后您可以编写一个递归函数match
,其大致工作原理如下(伪代码):
boolean match(ip: String,map: Map<String,Map<...>>) {
if (ip.empty) return true // done
first,rest = ip.splitfirst
if (first == "X") {
return map.values().any(submap -> match(rest,submap))
} else {
return first in map && match(rest,map[first])
}
}
这应该将复杂度从O(n)降低到O(log n);越多,您越需要分支出去,但是对于X.X.X.123
最多为O(n)(X.X.X.X
也是微不足道的)。对于较小的集合,正则表达式可能会更快,因为它具有较少的开销,但是对于较大的集合,这应该更快。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。