如何解决存储重复字符串元素的最优雅方式,在每个重复元素的末尾增加计数器
我想将重复项 String[]
转换为 List<String>
,其中每个下一个重复的元素都会在其名称后增加计数器。
示例:
Test,TestA,TestB,Test,TestB
应转换为:
Test,Test1,TestA1,TestA2,TestB1
结果必须与输入数据具有相同的顺序,仅添加计数器。
目前我写了这样的东西,但我不认为这是一种优雅有效的方法,我正在寻找更合适的方法:
String[] header = new String[]{"Test","TestA","TestB","Test","TestB"};
List<String> newHeader = new ArrayList<>();
for (String column : header) {
if (!newHeader.contains(column)) {
newHeader.add(column);
} else {
int counter = 1;
String columnIterName = column + counter;
while (newHeader.contains(columnIterName)) {
columnIterName = column + counter;
counter++;
}
newHeader.add(columnIterName);
}
}
return newHeader;
解决方法
假设您只关心原始列表中的重复项:
如果您使用 Map 来跟踪重复项,您可以将代码简化为:
public static List<String> some_method(){
String[] header = new String[]{"Test","TestA","TestB","Test","TestB"};
Map<String,Integer> track = new HashMap<>();
List<String> newHeader = new ArrayList<>();
for(String s : header){
Integer count = track.get(s);
if(count != null){
count++;
track.put(s,count);
newHeader.add(s + count);
}
else{
track.put(s,0);
newHeader.add(s);
}
}
return newHeader;
}
这样你也可以避免内部的 while 循环。
如果您还关心正在构建的列表中的重复项,例如对于输入:
Test Test Test1
你期望的输出:
Test Test1 Test11
然后您可以使用以下内容:
public static List<String> some_method(){
String[] header = new String[]{"Test","Test1"};
Map<String,Integer> track = new HashMap<>();
List<String> newHeader = new ArrayList<>();
for(String s : header){
Integer count = track.get(s);
if(count != null){
count++;
track.put(s,count);
newHeader.add(s + count);
track.put(s + count,0);
}
else{
track.put(s,0);
newHeader.add(s);
}
}
return newHeader;
}
,
从 Java 8 开始,Map
具有允许在缺少键时计算值的方法,例如,Map::compute
允许使用 BiFunction
增加计数器:
更新:处理特定测试:"Test","Test1"
当由于附加计数器而创建重复项时,应使用另一种方法 putIfAbsent
。
static List<String> convert(String ... header) {
Map<String,Integer> counters = new HashMap<>();
List<String> result = new ArrayList<>();
for (String column : header) {
int count = counters.compute(column,(k,v) -> v == null ? 0 : v + 1);
String toAdd = column + (count == 0 ? "" : Integer.toString(count));
counters.putIfAbsent(toAdd,0);
result.add(toAdd);
}
return result;
}
使用 Stream API 的类似解决方案可能如下所示。
更新:此处以“作弊模式”调用 putIfAbsent
(使用 peek
)
static List<String> convertStream(String ... header) {
Map<String,Integer> counters = new HashMap<>();
return Arrays.stream(header)
.map(column -> column +
(counters.compute(column,v) -> v == null ? 0 : v + 1) > 0
? Integer.toString(counters.get(column)) : "")
)
.peek(column -> counters.putIfAbsent(column,0))
.collect(Collectors.toList());
}
测试:
String[][] tests = {
{"Test","Test1"},{"Test","Test1","TestB"}
};
for (String[] test : tests) {
System.out.println("convert: " + convert(test));
System.out.println("stream : " + convertStream(test));
System.out.println("dreamcr: " + some_method(test));
System.out.println("------------\n");
}
输出:
convert: [Test,Test1,Test11]
stream : [Test,Test11]
dreamcr: [Test,Test11]
------------
convert: [Test,TestA,TestB,Test11,TestA1,TestA2,TestB1]
stream : [Test,TestB1]
dreamcr: [Test,TestB1]
------------
,
ArrayList
似乎不是匹配字符串的最佳选择,因为搜索 (.contains
) 效率不高。依次尝试所有后缀是一种很大的浪费。我想 Dictionary
会更好。
那么逻辑是
-
扫描列表
-
如果字符串不在字典中,则以count 1输入
-
else 增加其计数并将其附加到字符串(在原始列表中就地)。
-
您可以简化为以下内容:
public class Counter {
public static List<String> someMethod(String[] header){
Map<String,Integer> track = new HashMap<>();
List<String> newHeader = new ArrayList<>();
for(String s : header){
int count = track.compute(s,v) -> (v == null) ? 0 : v + 1);
if(count != 0)
track.put(s + count,0);
newHeader.add(count == 0 ? s : s + count);
}
return newHeader;
}
public static void main(String[] args) {
someMethod(new String[]{"Test","Test1"}).forEach(System.out::println);
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。