当前,我有这个方法,我想将其转换为Java 8流样式(我对此API btw几乎没有实践,这是此小练习的目的):
private static Map<Integer, List<String>> splitByWords(List<String> list) {
for (int i = 0; i < list.size(); i++) {
if(list.get(i).length() > 30 && list.get(i).contains("-")) {
mapOfElements.put(i, Arrays.stream(list.get(i).split("-")).collect(Collectors.toList()));
} else if(list.get(i).length() > 30) {
mapOfElements.put(i, Arrays.asList(new String[]{list.get(i)}));
} else {
mapOfElements.put(i, Arrays.asList(new String[]{list.get(i) + "|"}));
}
}
return mapOfElements;
}
这是我到目前为止所得到的:
private static Map<Integer, List<String>> splitByWords(List<String> list) {
Map<Integer, List<String>> mapOfElements = new HashMap<>();
IntStream.range(0, list.size())
.filter(i-> list.get(i).length() > 30 && list.get(i).contains("-"))
.boxed()
.map(i-> mapOfElements.put(i, Arrays.stream(list.get(i).split("-")).collect(Collectors.toList())));
//Copy/paste the above code twice, just changing the filter() and map() functions?
以“老式”方式,我只需要进行一次for
迭代就可以完成有关条件的一切操作。有没有一种方法可以使用Stream API来实现,或者,如果我想坚持下去,就必须重复上述代码,只是更改filter()和map()条件,因此需要进行三个for
迭代?
答案 0 :(得分:4)
当前带有for循环的解决方案看起来不错。由于您仅需区分三种情况,因此无需对处理进行概括。
如果需要区分更多的情况,则可以重构代码。我的方法是显式定义不同的条件及其相应的字符串处理。让我用问题代码解释一下。
首先,我使用枚举定义不同的条件。
$(document).ready(function() {
$("#submit").click(function(e) {
e.preventDefault();
var to = $("#to").val();
var subject = $("#subject").val();
var message = $("#message").val();
var data = {
'to': to,
'subject': subject,
'message': message
}
$.ajax({
type: "POST",
url: 'https://xxxxxxxx.execute-api.us-east-1.amazonaws.com/prod/xxxxx',
contentType: 'application/json',
data: JSON.stringify({data}),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
success: function(res){
console.log('Email was sent.');
},
error: function(){
console.log('Error.');
}
});
})
});
使用该枚举,我定义了相应的字符串处理器:
public enum StringClassification {
CONTAINS_HYPHEN, LENGTH_GT_30, DEFAULT;
public static StringClassification classify(String s) {
if (s.length() > 30 && s.contains("-")) {
return StringClassification.CONTAINS_HYPHEN;
} else if (s.length() > 30) {
return StringClassification.LENGTH_GT_30;
} else {
return StringClassification.DEFAULT;
}
}
}
基于此,我可以使用请求的 private static final Map<StringClassification, Function<String, List<String>>> PROCESSORS;
static {
PROCESSORS = new EnumMap<>(StringClassification.class);
PROCESSORS.put(StringClassification.CONTAINS_HYPHEN, l -> Arrays.stream(l.split("-")).collect(Collectors.toList()));
PROCESSORS.put(StringClassification.LENGTH_GT_30, l -> Arrays.asList(new String[] { l }));
PROCESSORS.put(StringClassification.DEFAULT, l -> Arrays.asList(new String[] { l + "|" }));
}
进行整个处理:
IntStream
方法是为字符串检索适当的 private static Map<Integer, List<String>> splitByWords(List<String> list) {
return IntStream.range(0, list.size()).boxed()
.collect(Collectors.toMap(Function.identity(), i -> PROCESSORS.get(StringClassification.classify(list.get(i))).apply(list.get(i))));
}
,然后依次检索相应的字符串处理器。字符串处理器通过提供StringClassification
来实现strategy pattern,该Function<String, List<String>>
根据String
将List<String>
映射到StringClassification
。
一个简单的例子:
public static void main(String[] args) {
List<String> list = Arrays.asList("123",
"1-2",
"0987654321098765432109876543211",
"098765432109876543210987654321a-b-c");
System.out.println(splitByWords(list));
}
输出为:
{0=[123|], 1=[1-2|], 2=[0987654321098765432109876543211], 3=[098765432109876543210987654321a, b, c]}
这使得添加或删除条件和字符串处理器变得容易。
答案 1 :(得分:3)
首先,当键是索引时,我看不出使用Map<Integer, List<String>>
类型的任何理由。为什么不使用List<List<String>>
呢?如果您不使用过滤器,则元素应与输入位于相同的索引上。
更实用的方法的强大之处在于它使您所做的工作更具可读性。因为您想对多个大小的字符串执行多项操作,所以很难编写一个干净的解决方案。但是,您可以在一个循环中完成此操作:
private static List<List<String>> splitByWords(List<String> list)
{
return list.stream()
.map(
string -> string.length() > 30
? Arrays.asList(string.split("-"))
: Arrays.asList(string + "|")
)
.collect(Collectors.toList());
}
您可以通过制作lambda多行来添加更复杂的逻辑(在这种情况下不需要)。例如。
.map(string -> {
// your complex logic
// don't forget, when using curly braces you'll
// need to return explicitly
return result;
})
更实用的方法是按大小对字符串进行分组,然后为不同的组应用特定的处理程序。很难保持索引不变,因此我将返回值更改为Map<String, List<String>>
,以便可以通过提供原始字符串来获取结果:
private static Map<String, List<String>> splitByWords(List<String> list)
{
Map<String, List<String>> result = new HashMap<>();
Map<Boolean, List<String>> greaterThan30;
// group elements
greaterThan30 = list.stream().collect(Collectors.groupingBy(
string -> string.length() > 30
));
// handle strings longer than 30 chars
result.putAll(
greaterThan30.get(true).stream().collect(Collectors.toMap(
Function.identity(), // the same as: string -> string
string -> Arrays.asList(string.split("-"))
))
);
// handle strings not longer than 30 chars
result.putAll(
greaterThan30.get(false).stream().collect(Collectors.toMap(
Function.identity(), // the same as: string -> string
string -> Arrays.asList(string + "|")
))
);
return result;
}
以上内容看起来很麻烦,但我认为更好理解。知道提供的字符串始终符合条件,您还可以将处理大和小的字符串的逻辑分派给其他方法。
这比第一个解决方案要慢。对于大小为n
的列表,它必须遍历n
元素以按条件分组。然后,循环遍历符合条件的x
(0 <= x <= n
)个元素,然后循环遍历不符合条件的n - x
个元素。 (总共是整个列表的2倍。)
在这种情况下,可能不值得麻烦,因为标准和适用的逻辑都非常简单。