如何使用流将此程序转换为java 8function样式?

问题
我编写了一个程序来查找给定字符串的字符的大写和小写的所有可能性。

一个例子是,输入 – “ab”/“Ab”等 – 任何一个输出 – [“ab”,“Ab”,“aB”,“AB”]

算法不正确 – 请在下方查看。

public static ArrayList permuteUCLC(String a) { String s=new String(a.toLowerCase()); ArrayList arr = new ArrayList(); arr.add(a); int l = a.length(); for(int i=0;i<=l;i++) { for(int j=i+1;j<=l;j++) { arr.add(s.substring(0,i)+s.substring(i,j).toUpperCase()+s.substring(j,l)); } } Collections.sort(arr); Collections.reverse(arr); return arr; } 

警告
在提出问题后我意识到我的算法是错误的。 我会在适当的时候尝试上传正确的算法。


子序列代码(正确代码)这是用于查找所有子序列并对其进行上限的代码。 假设所有字符都是唯一的。 如何查找索引并以function方式实现?

 public static void permuteSubsequence(String a) { int n=a.length(); for(int i=0;i<(1<<n);i++) { String buff=""; for(int j=0;j<n;j++) { if(((1<<j)&i)!=0) { buff=buff+new Character(a.charAt(j)).toString().toUpperCase(); } else { buff = buff + a.charAt(j); } } System.out.println(buff); } } 

从上述案例中挑选指数。 即,1的索引和大写的大写。


请求

如何使用Java流将上面的代码转换为函数样式?

我面临的问题是在map方法中模拟索引范围。 另外,有没有办法生成Strings的Stream,用于将相同的字符串复制到所有元素中,类似于IntStream.range(a,b)

 public static List permuteStreams(String a) { int l=(int)(Math.pow(2,a.length())-1) ArrayList al = new ArrayList(); for(int i=0;i<=l;i++) al.add(a);//Generate a stream and copy back into arraylist maybe if possible? List sl = al.stream() .map()//incomplete code .collect(Collectors.toList()); return sl; } 

不要通过拆分和连接来排列字符串,这是非常昂贵的操作,完全没有必要。 考虑到“大写”和“小写”恰好是两个状态,两个状态项的置换组合应该响铃,我们正在讨论二进制数 。 计算机中的整数是具有两种状态的位的组合,并且遍历这些位的所有可能的排列就像迭代整数范围一样容易。

即,范围0, 1, 2, 3, 4, 5, 6, 7的二进制数表示是000, 001, 010, 011, 100, 101, 110, 111 。 现在想象0代表“小写”, 1代表“大写”代表三个字符串,你差不多完成了。

因此,剩下的任务是根据是否设置关联位将String的字符转换为大写或小写。 有几种方法可以实现这一目标。 以下代码创建一个最初的小写字符串作为所有迭代的起始点,如果设置了一个位,则将字符切换为大写:

 public static void permuteSubsequence(String s) { if(s.isEmpty()) { System.out.println(); return; } String lower = s.toLowerCase(), upper = s.toUpperCase(); if(s.length()!=lower.length() || s.length()!=upper.length()) throw new UnsupportedOperationException("non trivial case mapping"); LongStream.range(0, 1L< { StringBuilder sb=new StringBuilder(lower); BitSet.valueOf(new long[] { l }).stream() .forEach(ix -> sb.setCharAt(ix, upper.charAt(ix))); return sb.toString(); }) .forEach(System.out::println); } 

请注意,此实现仅通过置换较长字符串的前62字符来欺骗,因为用于迭代的签名long不允许更多,但是排列62字符已经允许4611686018427387904组合,因此即使我们假设打印一个变体只需要一个纳秒,我们需要超过一百年的时间来打印它们。 所以你永远不会注意到作弊行为。

字符串的大写/小写转换不必生成相同长度的字符串。 此实现将拒绝具有非平凡案例映射的字符串,对于这种情况,这种排列是不可能的。

要改进的一件事是省略不具有不同大写和小写forms的字符。 这可以通过首先识别可置换字符(它们的位置)并仅置换这些字符来完成:

 public static void permuteSubsequence(String s) { int[] permutable = IntStream.range(0, s.length()) .filter(i->Character.toLowerCase(s.charAt(i))!=Character.toUpperCase(s.charAt(i))) .toArray(); if(permutable.length == 0) { System.out.println(s); return; } String lower = s.toLowerCase(), upper = s.toUpperCase(); if(s.length()!=lower.length() || s.length()!=upper.length()) throw new UnsupportedOperationException("non trivial case mapping"); LongStream.range(0, 1L< { StringBuilder sb=new StringBuilder(lower); BitSet.valueOf(new long[] { l }).stream() .map(bit -> permutable[bit]) .forEach(ix -> sb.setCharAt(ix, upper.charAt(ix))); return sb.toString(); }) .forEach(System.out::println); } 

有了这个, permuteSubsequence("Mr.X"); 将打印

 mr.x Mr.x mR.x MR.x mr.X Mr.X mR.X MR.X 

我面临的问题是在map方法中模拟索引范围。

更改问题 – 使用IntStream.range (或rangeClosed )从索引开始 。 有关更多信息,请参阅此答案 。 这是原始代码的(主要)面向流的版本:

 import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.stream.IntStream; import java.util.stream.Stream; import static java.util.function.Function.identity; import static java.util.stream.Collectors.toList; public static List permuteUCLCStream(String a) { String s = a.toLowerCase(); int l = a.length(); Stream result = IntStream .rangeClosed(0, l) .mapToObj(i -> IntStream .rangeClosed(i + 1, l) .mapToObj(j -> s.substring(0, i) + s.substring(i, j).toUpperCase() + s.substring(j, l))) .flatMap(identity()); List arr = Stream.concat(Stream.of(a), result).sorted().collect(toList()); Collections.reverse(arr); return arr; } 

有一些事情需要考虑。 首先,构建流api时假设流中的位置与您对每个元素执行的操作无关。 为了自然地适应这种情况,您需要重新设计算法而不关心索引号。

其次,您没有将任何单个字母映射到任何特定结果(或结果集),因此使用输入字符串字符的流不是特别有用。 你应该寻找其他东西来使用流。

你为固定的非流方法提出的算法实际上非常适合流式传输 – 在流中粘贴buff的内容并通过流api对其进行所有更改。 我认为这应该做的伎俩:

 public static List permuteStreams(String a) { Stream partialSolutions = Stream.of(new StringBuilder(a.length())); for (char c : a.toCharArray()) { partialSolutions = partialSolutions.flatMap(solution -> Stream.of( new StringBuilder(solution).append(Character.toLowerCase(c)), solution.append(Character.toUpperCase(c)))); } return partialSolutions.map(StringBuilder::toString).collect(Collectors.toList()); } 

或者,如果您不介意大约两倍的字符串复制操作:

 public static List permuteStreams(String a) { Stream partialSolutions = Stream.of(""); for (char c : a.toCharArray()) { partialSolutions = partialSolutions.flatMap(solution -> Stream.of( solution + Character.toLowerCase(c), solution + Character.toUpperCase(c)); } return partialSolutions.collect(Collectors.toList()); } 

实际上可以通过这种方式节省一些空间,因为StringBuilder在添加时分配额外的容量而你不会这样做。