使用Java 8 lambdas,&#34; best&#34;如果有List<T>
个可能的密钥和List<K>
,有效创建新Map<K,V>
的方法?在这种情况下,您可以获得List
个Map
个密钥,并且预计会生成List<T>
,其中T
是基于某些方面构建的某种类型的V
filter
,地图值类型。
我已经探索过一些并且感觉不舒服声称一种方式比另一种方式更好(可能有一个例外 - 参见代码)。我会澄清&#34;最好&#34;作为代码清晰度和运行时效率的组合。这些是我想出来的。我确信有人可以做得更好,这是这个问题的一个方面。我不喜欢大多数List
方面,因为这意味着需要在名称public class Java8Mapping {
private final Map<String,Wongo> nameToWongoMap = new HashMap<>();
public Java8Mapping(){
List<String> names = Arrays.asList("abbey","normal","hans","delbrook");
List<String> types = Arrays.asList("crazy","boring","shocking","dead");
for(int i=0; i<names.size(); i++){
nameToWongoMap.put(names.get(i),new Wongo(names.get(i),types.get(i)));
}
}
public static void main(String[] args) {
System.out.println("in main");
Java8Mapping j = new Java8Mapping();
List<String> testNames = Arrays.asList("abbey", "froderick","igor");
System.out.println(j.getBongosExample1(testNames).stream().map(Bongo::toString).collect(Collectors.joining(", ")));
System.out.println(j.getBongosExample2(testNames).stream().map(Bongo::toString).collect(Collectors.joining(", ")));
System.out.println(j.getBongosExample3(testNames).stream().map(Bongo::toString).collect(Collectors.joining(", ")));
System.out.println(j.getBongosExample4(testNames).stream().map(Bongo::toString).collect(Collectors.joining(", ")));
System.out.println(j.getBongosExample5(testNames).stream().map(Bongo::toString).collect(Collectors.joining(", ")));
System.out.println(j.getBongosExample6(testNames).stream().map(Bongo::toString).collect(Collectors.joining(", ")));
}
private static class Wongo{
String name;
String type;
public Wongo(String s, String t){name=s;type=t;}
@Override public String toString(){return "Wongo{name="+name+", type="+type+"}";}
}
private static class Bongo{
Wongo wongo;
public Bongo(Wongo w){wongo = w;}
@Override public String toString(){ return "Bongo{wongo="+wongo+"}";}
}
// 1: Create a list externally and add items inside 'forEach'.
// Needs to externally reference Map and List
public List<Bongo> getBongosExample1(List<String> names){
final List<Bongo> listOne = new ArrayList<>();
names.forEach(s -> {
Wongo w = nameToWongoMap.get(s);
if(w != null) {
listOne.add(new Bongo(nameToWongoMap.get(s)));
}
});
return listOne;
}
// 2: Use stream().map().collect()
// Needs to externally reference Map
public List<Bongo> getBongosExample2(List<String> names){
return names.stream()
.filter(s -> nameToWongoMap.get(s) != null)
.map(s -> new Bongo(nameToWongoMap.get(s)))
.collect(Collectors.toList());
}
// 3: Create custom Collector
// Needs to externally reference Map
public List<Bongo> getBongosExample3(List<String> names){
Function<List<Wongo>,List<Bongo>> finisher = list -> list.stream().map(Bongo::new).collect(Collectors.toList());
Collector<String,List<Wongo>,List<Bongo>> bongoCollector =
Collector.of(ArrayList::new,getAccumulator(),getCombiner(),finisher, Characteristics.UNORDERED);
return names.stream().collect(bongoCollector);
}
// example 3 helper code
private BiConsumer<List<Wongo>,String> getAccumulator(){
return (list,string) -> {
Wongo w = nameToWongoMap.get(string);
if(w != null){
list.add(w);
}
};
}
// example 3 helper code
private BinaryOperator<List<Wongo>> getCombiner(){
return (l1,l2) -> {
l1.addAll(l2);
return l1;
};
}
// 4: Use internal Bongo creation facility
public List<Bongo> getBongosExample4(List<String> names){
return names.stream().filter(s->nameToWongoMap.get(s) != null).map(s-> new Bongo(nameToWongoMap.get(s))).collect(Collectors.toList());
}
// 5: Stream the Map EntrySet. This avoids referring to anything outside of the stream,
// but bypasses the lookup benefit from Map.
public List<Bongo> getBongosExample5(List<String> names){
return nameToWongoMap.entrySet().stream().filter(e->names.contains(e.getKey())).map(e -> new Bongo(e.getValue())).collect(Collectors.toList());
}
// 6: Plain-ol-java loop
public List<Bongo> getBongosExample6(List<String> names){
List<Bongo> bongos = new ArrayList<>();
for(String s : names){
Wongo w = nameToWongoMap.get(s);
if(w != null){
bongos.add(new Bongo(w));
}
}
return bongos;
}
}
上创建中间结构和多次传递。现在,我选择了例6 - 一个普通的循环。 (注意:代码注释中有一些神秘的想法,特别是&#34;需要外部引用...&#34;这意味着从lambda外部。)
{{1}}
答案 0 :(得分:11)
如果namesToWongoMap
是一个实例变量,则无法真正避免捕获lambda。
您可以通过将操作分开一点来清理流:
return names.stream()
.map(n -> namesToWongoMap.get(n))
.filter(w -> w != null)
.map(w -> new Bongo(w))
.collect(toList());
return names.stream()
.map(namesToWongoMap::get)
.filter(Objects::nonNull)
.map(Bongo::new)
.collect(toList());
这样你就不会两次打电话给get
。
这与for
循环非常相似,但是,例如,如果namesToWongoMap
无法同时进行变异,理论上它可以并行化。
我不喜欢大多数人的
filter
方面,因为这意味着需要在姓名List
上创建中间结构和多次传递。
没有中间结构,List
只有一次通过。流管道为每个元素说&#34; ...执行这个操作序列&#34;。访问每个元素一次并应用管道。
以下是java.util.stream
package description的一些相关引用:
流不是存储元素的数据结构;相反,它通过计算操作管道传递来自数据结构,数组,生成器函数或I / O通道等源的元素。
懒洋洋地处理流可以显着提高效率;在上面的filter-map-sum示例的管道中,过滤,映射和求和可以融合到数据的单个传递中,具有最小的中间状态。
答案 1 :(得分:7)
Radiodef's answer几乎已经钉了它。给出的解决方案:
return names.stream()
.map(namesToWongoMap::get)
.filter(Objects::nonNull)
.map(Bongo::new)
.collect(toList());
可能是Java 8中最好的。
但是,我确实想提一下这里的小皱纹。如果地图中没有该名称,则Map.get
调用会返回null
,之后会将其过滤掉。这个本身没有任何问题,尽管它确实将无意义 - 不存在的语义烘焙到管道结构中。
在某种意义上,我们需要一个映射器管道操作,可以选择返回零个或一个元素。使用流进行此操作的方法是使用flatMap
。 flatmapper函数可以将任意数量的元素返回到流中,但在这种情况下,我们只需要零或一。以下是如何做到这一点:
return names.stream()
.flatMap(name -> {
Wongo w = nameToWongoMap.get(name);
return w == null ? Stream.empty() : Stream.of(w);
})
.map(Bongo::new)
.collect(toList());
我承认这很笨重,所以我不建议这样做。这是一个略好但有些模糊的方法:
return names.stream()
.flatMap(name -> Optional.ofNullable(nameToWongoMap.get(name))
.map(Stream::of).orElseGet(Stream::empty))
.map(Bongo::new)
.collect(toList());
但我仍然不确定我是否会按原样推荐这个。
flatMap
的使用确实指出了另一种方法。如果你有一个更复杂的策略来处理不存在的情况,你可以将它重构为一个辅助函数,它返回一个包含结果的Stream或一个空的Stream,如果没有结果的话。
最后,JDK 9 - 在撰写本文时仍在开发中 - 添加了Stream.ofNullable
,这在以下情况下非常有用:
return names.stream()
.flatMap(name -> Stream.ofNullable(nameToWongoMap.get(name)))
.map(Bongo::new)
.collect(toList());
另外,JDK 9还添加了Optional.stream
,它从Optional
创建了一个零或一个流。如果要从flatMap
内调用Optional-returning函数,这非常有用。有关详细信息,请参阅this answer和this answer。
答案 2 :(得分:3)
我没有看到的一种方法是SELECT DISTINCT SCode INTO #TEMP1 FROM XTransactions_01
WHERE Last_Mdt > '2012-01-01'
AND SCode IS NOT NULL
SELECT * FROM XSales_Code SC
WHERE SC.Status = 1
AND SC.SCode NOT IN
(
SELECT Scode FROM #TEMP1
)
AND SC.Last_Mdt < '2014-01-01'
ORDER BY Last_Mdt desc
DROP TABLE #TEMP1
:
{{1}}
额外的Map是最小的性能命中,因为它只是复制指向对象的指针,而不是对象本身。