从流中获取中间结果以供稍后在流中使用
Getting intermediate results from stream to be used later in stream
我正在尝试编写一些函数式编程代码(使用来自 Java 8 的 lambda 和流)来测试字符串中是否包含唯一字符(如果 它有, return true
, 如果 不 , return false
)。使用 vanilla Java 执行此操作的常见方法是使用类似集合的数据结构,即:
public static boolean oldSchoolMethod(String str) {
Set<String> set = new HashSet<>();
for(int i=0; i<str.length(); i++) {
if(!set.add(str.charAt(i) + "")) return false;
}
return true;
}
集合 returns true
如果 character/object 可以添加到集合中(因为它以前不存在)。它 returns false
如果它不能(它已经存在于集合中,重复值,并且不能添加)。这使得打破循环并检测是否有重复变得容易,而无需遍历字符串的所有长度 N 个字符。
我知道在 Java 8 个流中你不能分开一个流。有没有办法 捕获 中间流操作的 return 值,比如添加到集合的 return 值(true
或 false
) 并将该值发送到管道的下一阶段(另一个中间操作或终端流操作)?即
Arrays.stream(myInputString.split(""))
.forEach( i -> {
set.add(i) // need to capture whether this returns "true" or "false" and use that value later in
// the pipeline or is this bad/not possible?
});
我想到的解决这个问题的其他方法之一是只使用 distinct()
并将结果收集到一个新字符串中,如果它与原始字符串的长度相同,那么你就知道了是唯一的,否则如果有不同的长度,一些字符会因为不明显而被过滤掉,因此你知道在比较长度时它不是唯一的。我在这里看到的唯一问题是你必须遍历字符串的所有长度 N 字符,其中“老派”方法最好的情况可以在几乎恒定的时间内完成 O (1),因为它正在打破循环并在发现 1 个重复字符后立即 returning:
public static boolean java8StreamMethod(String str) {
String result = Arrays.stream(str.split(""))
.distinct()
.collect(Collectors.joining());
return result.length() == str.length();
}
此代码可能适合您:
public class Test {
public static void main(String[] args) {
String myInputString = "hellowrd";
HashSet<String> set = new HashSet<>();
Optional<String> duplicateChar =Arrays.stream(myInputString.split("")).
filter(num-> !set.add(num)).findFirst();
if(duplicateChar.isPresent()){
System.out.println("Not unique");
}else{
System.out.println("Unique");
}
}
}
这里使用 findFirst()
我能够找到第一个重复元素。这样我们就不需要继续迭代其余的字符了。
如果只映射到布尔值呢?
Arrays.stream(myInputString.split(""))
.map(set::add)
.<...>
我想这会解决你的具体问题,但这不是一个很好的解决方案,因为流链中的闭包不应该有副作用(这正是函数式编程的重点......)。
有时经典的 for 循环仍然是某些问题的更好选择;-)
您的解决方案都在执行不必要的字符串操作。
例如您可以使用 Set<Character>
:
而不是使用 Set<String>
public static boolean betterOldSchoolMethod(String str) {
Set<Character> set = new HashSet<>();
for(int i=0; i<str.length(); i++) {
if(!set.add(str.charAt(i))) return false;
}
return true;
}
但是从char
到Character
的拳击也是可以避免的。
public static boolean evenBetterOldSchoolMethod(String str) {
BitSet set = new BitSet();
for(int i=0; i<str.length(); i++) {
if(set.get(str.charAt(i))) return false;
set.set(str.charAt(i));
}
return true;
}
同样,对于 Stream 变体,您可以使用 str.chars()
而不是 Arrays.stream(str.split(""))
。此外,您可以使用 count()
而不是通过 collect(Collectors.joining())
将所有元素收集到一个字符串中,只需在其上调用 length()
。
解决这两个问题得到解决方案:
public static boolean newMethod(String str) {
return str.chars().distinct().count() == str.length();
}
这很简单,但是缺少短路。此外,distinct()
的性能特征是依赖于实现的。在 OpenJDK 中,它在后台使用普通的 HashSet
,而不是 BitSet
或类似的东西。
我正在尝试编写一些函数式编程代码(使用来自 Java 8 的 lambda 和流)来测试字符串中是否包含唯一字符(如果 它有, return true
, 如果 不 , return false
)。使用 vanilla Java 执行此操作的常见方法是使用类似集合的数据结构,即:
public static boolean oldSchoolMethod(String str) {
Set<String> set = new HashSet<>();
for(int i=0; i<str.length(); i++) {
if(!set.add(str.charAt(i) + "")) return false;
}
return true;
}
集合 returns true
如果 character/object 可以添加到集合中(因为它以前不存在)。它 returns false
如果它不能(它已经存在于集合中,重复值,并且不能添加)。这使得打破循环并检测是否有重复变得容易,而无需遍历字符串的所有长度 N 个字符。
我知道在 Java 8 个流中你不能分开一个流。有没有办法 捕获 中间流操作的 return 值,比如添加到集合的 return 值(true
或 false
) 并将该值发送到管道的下一阶段(另一个中间操作或终端流操作)?即
Arrays.stream(myInputString.split(""))
.forEach( i -> {
set.add(i) // need to capture whether this returns "true" or "false" and use that value later in
// the pipeline or is this bad/not possible?
});
我想到的解决这个问题的其他方法之一是只使用 distinct()
并将结果收集到一个新字符串中,如果它与原始字符串的长度相同,那么你就知道了是唯一的,否则如果有不同的长度,一些字符会因为不明显而被过滤掉,因此你知道在比较长度时它不是唯一的。我在这里看到的唯一问题是你必须遍历字符串的所有长度 N 字符,其中“老派”方法最好的情况可以在几乎恒定的时间内完成 O (1),因为它正在打破循环并在发现 1 个重复字符后立即 returning:
public static boolean java8StreamMethod(String str) {
String result = Arrays.stream(str.split(""))
.distinct()
.collect(Collectors.joining());
return result.length() == str.length();
}
此代码可能适合您:
public class Test {
public static void main(String[] args) {
String myInputString = "hellowrd";
HashSet<String> set = new HashSet<>();
Optional<String> duplicateChar =Arrays.stream(myInputString.split("")).
filter(num-> !set.add(num)).findFirst();
if(duplicateChar.isPresent()){
System.out.println("Not unique");
}else{
System.out.println("Unique");
}
}
}
这里使用 findFirst()
我能够找到第一个重复元素。这样我们就不需要继续迭代其余的字符了。
如果只映射到布尔值呢?
Arrays.stream(myInputString.split(""))
.map(set::add)
.<...>
我想这会解决你的具体问题,但这不是一个很好的解决方案,因为流链中的闭包不应该有副作用(这正是函数式编程的重点......)。
有时经典的 for 循环仍然是某些问题的更好选择;-)
您的解决方案都在执行不必要的字符串操作。
例如您可以使用 Set<Character>
:
Set<String>
public static boolean betterOldSchoolMethod(String str) {
Set<Character> set = new HashSet<>();
for(int i=0; i<str.length(); i++) {
if(!set.add(str.charAt(i))) return false;
}
return true;
}
但是从char
到Character
的拳击也是可以避免的。
public static boolean evenBetterOldSchoolMethod(String str) {
BitSet set = new BitSet();
for(int i=0; i<str.length(); i++) {
if(set.get(str.charAt(i))) return false;
set.set(str.charAt(i));
}
return true;
}
同样,对于 Stream 变体,您可以使用 str.chars()
而不是 Arrays.stream(str.split(""))
。此外,您可以使用 count()
而不是通过 collect(Collectors.joining())
将所有元素收集到一个字符串中,只需在其上调用 length()
。
解决这两个问题得到解决方案:
public static boolean newMethod(String str) {
return str.chars().distinct().count() == str.length();
}
这很简单,但是缺少短路。此外,distinct()
的性能特征是依赖于实现的。在 OpenJDK 中,它在后台使用普通的 HashSet
,而不是 BitSet
或类似的东西。