我有一个工作示例,找到第一个重复和 字符串中的非重复字符使用java 7
以下是工作示例
public class FindFirstRepeatedAndNonRepeatedChar {
static void firstRepeatedNonRepeatedChar(String inputString) {
HashMap<Character, Integer> charCountMap = new HashMap<Character, Integer>();
char[] strArray = inputString.toCharArray();
for (char c : strArray) {
if (charCountMap.containsKey(c)) {
charCountMap.put(c, charCountMap.get(c) + 1);
} else {
charCountMap.put(c, 1);
}
}
for (char c : strArray) {
if (charCountMap.get(c) == 1) {
System.out.println("First Non-Repeated Character In '" + inputString + "' is '" + c + "'");
break;
}
}
for (char c : strArray) {
if (charCountMap.get(c) > 1) {
System.out.println("First Repeated Character In '" + inputString + "' is '" + c + "'");
break;
}
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("Enter the string :");
String input = sc.next();
firstRepeatedNonRepeatedChar(input);
}
}
有人可以帮我解决如何使用Java8重构上述代码吗?
答案 0 :(得分:5)
通过一些有用的输入,我用更少的代码调整了我的答案:
public class FirstRepeat {
public static void main(String[] args) {
Map<Character, Long> collect = "abcsdnvs".chars().mapToObj(i -> (char)i).collect(Collectors.groupingBy(Function.identity(), LinkedHashMap::new, Collectors.counting()));
collect.forEach( (x,y) -> System.out.println( "Key: " + x + " Val: " + y));
Optional<Character> firstNonRepeat = collect.entrySet().stream().filter( (e) -> e.getValue() == 1).map(e -> e.getKey()).findFirst();
if(firstNonRepeat.isPresent()) {
System.out.println("First non repeating:" + firstNonRepeat.get());
}
Optional<Character> firstRepeat = collect.entrySet().stream().filter( (e) -> e.getValue() > 1).map(e -> e.getKey()).findFirst();
System.out.println("First repeating:" + firstRepeat.orElse(null));
}
}
以上是做什么的:
"abcsdnvs".chars().mapToObj(i -> (char)i)
.collect(Collectors.groupingBy(Function.identity(), LinkedHashMap::new, Collectors.counting()));
分组分为3个不同的部分:
正如所指出的,我可以使用Function.identity()。相当于x -> x
。这意味着我们按实际角色分组。
我们正在使用LinkedHashMap::new
。原因是我们需要保留插入顺序以便找到第一个元素。默认实现使用HashMap,它不会保留插入顺序。
由于我们正在使用分组,因此我们需要决定如何收集分组元素。在这种情况下,我们需要发生次数。为此,您可以使用:Collectors.counting()
,它将简单地总结给定角色可用的元素数量。
程序然后打印:
Key: a Val: 1
Key: b Val: 1
Key: c Val: 1
Key: s Val: 2
Key: d Val: 1
Key: n Val: 1
Key: v Val: 1
First non repeating:a
First repeating:s
我们正在使用流操作来查找第一个元素(基于过滤器):
Optional<Character> firstNonRepeat = collect.entrySet().stream().filter( (e) -> e.getValue() == 1).map(e -> e.getKey()).findFirst();
我们可以对分组元素进行流式处理,使用值(>1
表示第一次重复,==1
表示第一个非重复字符)。然后findFirst
方法返回Element,如果存在这样的元素。
返回的值是Optional,应该安全处理。如前所述,您可以使用isPresent()
检查是否找到了值(请参阅第一个print语句)或使用orElse(...)
返回默认值而不是抛出异常(请参阅print statement number 2 where我返回null作为默认值,以防止Optional在没有找到重复的字母的情况下抛出异常)
答案 1 :(得分:2)
请注意,即使对于当前仍然需要的传统,必需的解决方案,Java 8也提供了改进。以下成语:
if (charCountMap.containsKey(c)) {
charCountMap.put(c, charCountMap.get(c) + 1);
} else {
charCountMap.put(c, 1);
}
可以用简单的
替换charCountMap.merge(c, 1, Integer::sum);
这将放置指定的值(1
),如果没有先前的值或者使用先前的值和新的值评估指定的函数(这里是方便的方法引用Integer::sum
),获取存储的新值。
但是对于这项特定任务,HashMap<Character, Integer>
是过度的。首先,我们对实际计数不感兴趣,我们需要知道的是是否遇到了一个字符,以确定是否再次遇到过这个字符,这只是两位状态。由于char
s的值范围有限,我们可以轻松使用线性映射而不是散列。换句话说,两个BitSet
就足够了:
static void firstRepeatedNonRepeatedChar(String s) {
if(s.isEmpty()) {
System.out.println("empty string");
return;
}
BitSet seen=new BitSet(), repeated=new BitSet();
s.chars().forEachOrdered(c -> (seen.get(c)? repeated: seen).set(c));
if(repeated.isEmpty()) System.out.println("first unique: "+s.charAt(0));
else {
s.chars().filter(repeated::get).findFirst()
.ifPresent(c -> System.out.println("first repeated: "+(char)c));
s.chars().filter(c -> !repeated.get(c)).findFirst()
.ifPresent(c -> System.out.println("first unique: "+(char)c));
}
}
主要任务是遍历所有字符并设置位组seen
或repeated
中的位,具体取决于之前是否遇到过。
然后,如果没有重复的字符,任务很简单。从那时起,所有角色都是独一无二的,第一个角色也是第一个独特的角色。否则,我们只是再次迭代字符串,停在第一个字符,其repeated
位置位/未设置以获得第一个重复/唯一字符。
答案 2 :(得分:1)
这只是一个想法,也许是矫枉过正,但我喜欢Holger的答案,所以我想补充一点,只是为了完整性。
基于他的回答,我起草了一个快速的收集器impl,可以在整个过程中使用它来缩短它。
首先收集第一个非重复字符和第一个重复字符的静态收集器作为Optionals。它基于Holger已经指出的相同逻辑:
public class PairCollector {
public static
Collector<Character, ?, Pair<Optional<Character>,Optional<Character>>> get() {
return Collector.of(PairCollectorImpl::new, PairCollectorImpl::accumulate,
PairCollectorImpl::merge, PairCollectorImpl::finish);
}
private static final class PairCollectorImpl {
private BitSet seen=new BitSet();
private BitSet repeated=new BitSet();
private StringBuilder builder=new StringBuilder();
public void accumulate(Character val) {
builder.append(val);
(seen.get(val)? repeated: seen).set(val);
}
PairCollectorImpl merge(PairCollectorImpl other) {
builder.append(other.builder);
repeated.or(other.repeated);
other.seen.stream().forEach(c -> (seen.get(c)? repeated: seen).set(c));
return this;
}
public Pair<Optional<Character>, Optional<Character>> finish() {
return Pair.of(
builder.chars().filter(repeated::get).mapToObj(c -> (char)c).findFirst(),
builder.chars().filter(c -> !repeated.get(c))
.mapToObj(c -> (char)c).findFirst());
}
}
}
通过这种方式,您现在可以在一个流中处理这两个流并为您吐出结果,例如:
public class FirstRepeat {
public static void main(String[] args) {
Pair<Optional<Character>, Optional<Character>> collect = "asdbsjd".chars().mapToObj(c -> (char) c)
.collect(PairCollector.get());
collect.getLeft().ifPresent(c -> System.out.println(c));
collect.getRight().ifPresent(c -> System.out.println(c));
System.out.println();
List<Character> toTest = "asdbsjd".chars().mapToObj(c -> (char) c).collect(Collectors.toList());
Pair<Optional<Character>,Optional<Character>> collect2 = toTest.parallelStream().collect(PairCollector.get());
collect2.getLeft().ifPresent(c -> System.out.println(c));
collect2.getRight().ifPresent(c -> System.out.println(c));
}
}
打印哪些:
s
a
s
a
虽然编写自己的收集器可能有点过分,除非你在代码中的所有地方重复使用它:)