在每个重复元素的末尾增加计数器来存储重复的 String 元素的最优雅的方式
Most elegant way to store duplicate String elements with increasing counter at the end of every duplicated element
我想将重复项 String[]
转换为 List<String>
,其中每个下一个重复元素的名称后都会增加计数器。
示例:
Test, TestA, TestB, Test, TestA, TestA, TestB
应转换为:
Test, TestA, TestB, Test1, TestA1, TestA2, TestB1
结果必须与输入数据具有相同的顺序,只添加了计数器。
目前我写了类似这样的东西,但我不认为这样做是否优雅有效,我正在寻找更合适的方法:
String[] header = new String[]{"Test", "TestA", "TestB", "Test", "TestA", "TestA", "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;
假设您只关心原始列表中的重复项,那么:
如果您使用地图来跟踪重复项,您可以将代码简化为:
public static List<String> some_method(){
String[] header = new String[]{"Test", "TestA", "TestB", "Test", "TestA", "TestA", "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","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;
}
ArrayList
似乎不是匹配字符串的最佳选择,因为搜索 (.contains
) 效率不高。依次尝试所有后缀是一种很大的浪费。我猜 Dictionary
会更好。
那么逻辑就是
扫一扫列表
如果该字符串不在字典中,则输入第 1 个
否则增加其计数并将其附加到字符串(就地在原始列表中)。
由于 Java 8,Map
具有允许在缺少键的情况下计算值的方法,例如,Map::compute
允许使用 BiFunction
:
Update:处理特定测试:"Test", "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, (k, v) -> v == null ? 0 : v + 1) > 0
? Integer.toString(counters.get(column)) : "")
)
.peek(column -> counters.putIfAbsent(column, 0))
.collect(Collectors.toList());
}
测试:
String[][] tests = {
{"Test", "Test", "Test1"},
{"Test", "Test1", "TestA", "TestB", "Test", "Test1", "TestA", "TestA", "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, Test1, Test11]
dreamcr: [Test, Test1, Test11]
------------
convert: [Test, Test1, TestA, TestB, Test1, Test11, TestA1, TestA2, TestB1]
stream : [Test, Test1, TestA, TestB, Test1, Test11, TestA1, TestA2, TestB1]
dreamcr: [Test, Test1, TestA, TestB, Test1, Test11, TestA1, TestA2, TestB1]
------------
您可以简化为以下内容:
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, (k, 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","Test","Test1"}).forEach(System.out::println);
}
}
我想将重复项 String[]
转换为 List<String>
,其中每个下一个重复元素的名称后都会增加计数器。
示例:
Test, TestA, TestB, Test, TestA, TestA, TestB
应转换为:
Test, TestA, TestB, Test1, TestA1, TestA2, TestB1
结果必须与输入数据具有相同的顺序,只添加了计数器。
目前我写了类似这样的东西,但我不认为这样做是否优雅有效,我正在寻找更合适的方法:
String[] header = new String[]{"Test", "TestA", "TestB", "Test", "TestA", "TestA", "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;
假设您只关心原始列表中的重复项,那么:
如果您使用地图来跟踪重复项,您可以将代码简化为:
public static List<String> some_method(){
String[] header = new String[]{"Test", "TestA", "TestB", "Test", "TestA", "TestA", "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","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;
}
ArrayList
似乎不是匹配字符串的最佳选择,因为搜索 (.contains
) 效率不高。依次尝试所有后缀是一种很大的浪费。我猜 Dictionary
会更好。
那么逻辑就是
扫一扫列表
如果该字符串不在字典中,则输入第 1 个
否则增加其计数并将其附加到字符串(就地在原始列表中)。
由于 Java 8,Map
具有允许在缺少键的情况下计算值的方法,例如,Map::compute
允许使用 BiFunction
:
Update:处理特定测试:"Test", "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, (k, v) -> v == null ? 0 : v + 1) > 0
? Integer.toString(counters.get(column)) : "")
)
.peek(column -> counters.putIfAbsent(column, 0))
.collect(Collectors.toList());
}
测试:
String[][] tests = {
{"Test", "Test", "Test1"},
{"Test", "Test1", "TestA", "TestB", "Test", "Test1", "TestA", "TestA", "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, Test1, Test11]
dreamcr: [Test, Test1, Test11]
------------
convert: [Test, Test1, TestA, TestB, Test1, Test11, TestA1, TestA2, TestB1]
stream : [Test, Test1, TestA, TestB, Test1, Test11, TestA1, TestA2, TestB1]
dreamcr: [Test, Test1, TestA, TestB, Test1, Test11, TestA1, TestA2, TestB1]
------------
您可以简化为以下内容:
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, (k, 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","Test","Test1"}).forEach(System.out::println);
}
}