我有一个深度嵌套的配置麻烦。
问题恰好出现在机器学习中,最终用户调用交叉验证例程,可能会或可能不会指定任何各种参数(例如" randomSeed" = 17)
无论哪种方式,参数必须首先传递给交叉验证算法,然后传递给第一个机器学习算法。机器学习算法必须能够设置并传递其他参数,所有这些都不需要初始用户知道。
参数用户链中的大多数消费者都期望从一个java Map接口进行查找。
由于性能原因(CPU和内存)(' root key-name'空间)将被使用而无需修改数千次,并且每次都将按键展平到一个库中是没有吸引力的在传递包之前,需要指定许多其他参数。一个不错的模拟是PATH变量如何工作,路径中的每个元素都是一个目录(key-namespace)。当针对PATH变量进行查询时(例如,您在命令行键入' emacs'),它会按顺序查找该文件名(指定值)的每个目录(键的未命名命名空间) ,直到它找到它,或者找不到它。如果找到它,则可以执行它找到的可执行文件的特定内容(获取参数集的值)。如果您有另一个PATH变量,则可以在将PATH变量设置传递给新的最终用户时在其前面附加一个新目录(匿名密钥空间),而不修改以前的目录(首选项)。 / p>
鉴于配置参数上的名称空间实际上是平的,像Python ChainMap这样的解决方案将是完美的(例如example usage),但我找不到相应的解决方案在Java?
答案 0 :(得分:1)
周末我继续创建ChainMap
实施;感谢Java 8,这是一个非常小的课程。我的实施与你的实施略有不同;它没有尝试反映Python的行为,而是遵循Map
接口的规范。值得注意的是:
.containsValue()
与早期地图屏蔽的值不匹配。.put()
会返回链图的先前值,即使该值位于以后的地图中。.remove()
从所有地图中删除密钥,而不仅仅是第一个地图或可见条目。来自Javadoc:" 一旦呼叫返回,地图将不包含指定密钥的映射。" .clear()
清除所有地图,而不仅仅是顶部地图。.equals()
和.hashCode()
,以便它等于其他Map
实现。我也没有实现推/弹行为,因为它感觉像是反模式; ChainMap
已经是一系列地图的O(1)视图,您可以根据需要使用所需的地图构建其他ChainMap
。
显然,如果您的实施适用于您的用例,那就太棒了。但它在几个地方违反了Map
合同;我强烈建议删除implements Map<K, V>
,然后让它成为一个独立的类。
许多班级的方法都很好,例如:
@Override
public int size() {
return keySet().size();
}
@Override
public boolean isEmpty() {
return !chain.stream().filter(map -> !map.isEmpty()).findFirst().isPresent();
}
@Override
public boolean containsKey(Object key) {
return chain.stream().filter(map -> map.containsKey(key)).findFirst().isPresent();
}
@Override
public boolean containsValue(Object value) {
return entrySet().stream()
.filter(e -> value == e.getValue() || (value != null && value.equals(e.getValue())))
.findFirst().isPresent();
}
@Override
public V get(Object key) {
return chain.stream().filter(map -> map.containsKey(key))
.findFirst().map(map -> map.get(key)).orElse(null);
}
我已经写了一些tests来验证班级的行为。欢迎额外的测试用例。
我还扩展了使用Maps.asMap()
创建地图集合的不可变视图的想法;如果你不需要突变,这将很好地工作。 (作为I learned,您必须使用.reduce()
的三参数形式来使泛型行为表现。)
public static <K, V> Map<K, V> immutableChainView(
Iterable<? extends Map<? extends K, ? extends V>> maps) {
return StreamSupport.stream(maps.spliterator(), false).reduce(
(Map<K,V>)ImmutableMap.<K,V>of(),
(a, b) -> Maps.asMap(Sets.union(a.keySet(), b.keySet()),
k -> a.containsKey(k) ? a.get(k) : b.get(k)),
(a, b) -> Maps.asMap(Sets.union(a.keySet(), b.keySet()),
k -> a.containsKey(k) ? a.get(k) : b.get(k)));
}
答案 1 :(得分:0)
开箱即用,我不知道等效物。番石榴也不提供Maps.chain()
方法,但也许它应该。
对于正常用例,我只想构建一个新的Map
;使用番石榴,你可以简洁地这样做,例如:
// Entries in map2 overwrite map1; reverse the insertion order if needed
ImmutableMap.builder().putAll(map1).putAll(map2).build();
您可以通过扩展ChainMap<K, V>
来定义自己的List<Map<? extends K, ? extends V>>
实施,以便相对轻松地存储AbstractMap
。然而,这会引入O(k)
开销(其中k
是链中的映射数)。如果您的地图太大以至于复制太多,或者您打算经常构建这些链式映射,那么这是值得的,但我不会建议它。
对于合并属性这样的用例,我只想构建一个新的地图并完成它。
答案 2 :(得分:0)
由于似乎没有现成的东西(感谢lopisan和dimo414的提示/指针),我做了第一次破解实施,至少可以满足我的需求。如果有人知道图书馆级版本,我会暂时将此标记作为答案。
我已经包含了很多示例用法调用。它们可以转换为单元测试。有些地方可以提高效率。
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
/**
* Not thread safe. To make it thread safe, you'd also have to ensure the underlying collections are thread safe.
* Expected use case is indexing Strings
*/
public class ChainMap<K, V> implements Map<K, V>, Cloneable{
ArrayList< Map<K, V> > scopes = new ArrayList<>();
@Override
public V get(Object key) {
for( Map<K, V> m : Lists.reverse( scopes ))
if( m.containsKey( key)) return m.get(key);
//no one has it..
return null;
}
public void pushScope( Map< K, V> scope){
scopes.add( scope);
}
public Map<K, V> popScope( ) {
if( scopes.size() == 0) throw new RuntimeException("must have at least one underlying map in the Chain to do a pop");
return scopes.remove( scopes.size() -1);
}
/** warning, this risks being expensive, as the semantics are interpreted as the count of distinct keys.
* you may want to cache this value*/
public int size() { return keySet().size(); }
public boolean isEmpty() {
for( Map<K, V> m : scopes ) //no reverese iteration needed
if( !m.isEmpty()) return false;
return true;
}
public boolean containsKey(Object key) {
for( Map<K, V> m : scopes ) //no reverese iteration needed
if( m.containsKey( key)) return true;
return false;
}
public boolean containsValue(Object value) {
for( Entry<K, V> e : entrySet())
if( (value == e.getValue() || (value != null && value.equals(e.getValue()))) == true)
return true;
return false;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Map) {
return entrySet().equals(((Map<?,?>)obj).entrySet());
}
return false;
}
@Override
public int hashCode() {
return entrySet().hashCode();
}
public V put(K key, V value) {
if( scopes.size() == 0) throw new RuntimeException("must have at least one underlying map in the Chain to do a put");
return scopes.get( scopes.size()-1).put( key, value);
}
public V remove(Object key) {
return scopes.get( scopes.size()-1).remove( key);
}
public void putAll(Map<? extends K, ? extends V> m) {
if( scopes.size() == 0) throw new RuntimeException("must have at least one underlying map in the Chain to do a put");
scopes.get( scopes.size()-1).putAll( m);
}
/** clears only the last, by default */
public void clear() {
scopes.get( scopes.size()-1).clear();
}
/** builds the result as a view on the underlying keySet */
public Set<K> keySet() {
int numMaps = scopes.size();
if( numMaps == 0) return Collections.emptySet();
else if( numMaps == 1) return scopes.get(0).keySet();
else{
Set<K> result = Sets.union( scopes.get(numMaps-1).keySet(), scopes.get(numMaps-2).keySet());
for (int i = scopes.size()-3; i >=0 ; i--)
result = Sets.union( result, scopes.get( i).keySet());
return result;
}
}
public Collection<V> values() {
return this.entrySet().stream().map( e -> e.getValue()).collect(Collectors.toList());
}
/** builds the result as a view on the underlying entrySets */
public Set<Map.Entry<K, V>> entrySet() {
int numMaps = scopes.size();
if( numMaps == 0) return new HashMap<K, V>().entrySet();
else if( numMaps == 1) return scopes.get(0).entrySet();
else{
Set<K> keySet = this.keySet();
Map<K, V> m = Maps.asMap( keySet, key -> this.get(key));
return m.entrySet(); //return Maps.asMap( keySet, key -> this.get(key)).keySet();
}
}
@SuppressWarnings("unchecked")
public Object clone(){
try {
ChainMap< K, V> cm = (ChainMap< K, V>) super.clone();
cm.scopes = (ArrayList< Map<K, V> > ) this.scopes.clone();
return cm;
} catch (CloneNotSupportedException e) {
throw new Error( e );
}
}
public ChainMap<K, V> copy(){
@SuppressWarnings("unchecked")
ChainMap<K, V> c = (ChainMap<K, V>) clone();
return c;
}
public static void
examples1
( )
{
ChainMap<String, Object> cm1 = new ChainMap<>();
HashMap<String, Object> a = new HashMap<>();
a.put( "a", "A");
a.put( "b", "B");
a.put( "c", "C");
a.put( "m", "M");
a.put( "a'sMap", "asValue"); //<-- tracer entry
HashMap<String, Object> b = new HashMap<>();
b.put( "c", "CCC");
b.put( "b'sMap", "bsValue"); //<-- tracer entry
HashMap<String, Object> c = new HashMap<>();
c.put( "a", "AAA");
c.put( "b", 1);
c.put( "z", "ZZZ");
c.put( "c'sMap", "csMapValue"); //<-- tracer entry
cm1.pushScope( a);
cm1.pushScope( b);
cm1.pushScope( c);
PrintStream o = System.out;
o.println( cm1.get( "z")); //prints "ZZZ"
o.println( cm1.get( "b")); //prints 1
cm1.put( "z", 5);
o.println( cm1.get( "z")); //prints 5
ChainMap<String, Object> cm2 = cm1.copy();
HashMap<String, Object> d = new HashMap<>();
d.put( "a", 999);
d.put( "w", "WWWWWWW");
d.put( "x", "XXXXXXX");
d.put( "t", "TTTTTTT");
d.put( "d'sMap", "dsMapValue"); //<-- tracer entry
cm2.pushScope(d);
ChainMap<String, Object> cm3 = cm2.copy();
o.println( cm2.get( "a")); //prints "999"
o.println( cm1.get( "a")); //prints "AAA"
cm2.popScope();
cm2.popScope();
o.println( cm2.get("a"));//prints "A"
o.println( cm3.keySet().size());
o.println( "__________");
//show how can iterate keys-value pairs
for( Entry<String, Object> e: cm3.entrySet())
o.println( e.getKey() + ":" + e.getValue());
o.println( "__________");
o.println( cm3.keySet().contains( "w")); //prints true
o.println( cm3.containsKey( "f")); //prints false
o.println( cm3.containsKey( "a")); //prints true
o.println( cm3.containsKey( "w")); //prints true
cm3.popScope();
o.println( cm3.containsKey( "w")); //prints false
}
public static void
examples2
( )
{
ChainMap<String, Object> cm1 = new ChainMap<>();
HashMap<String, Object> a = new HashMap<>();
a.put( "a", "A");
a.put( "a'sMap", "asValue");
HashMap<String, Object> b = new HashMap<>();
b.put( "b", "BBB");
b.put( "b'sMap", "bsValue");
HashMap<String, Object> c = new HashMap<>();
c.put( "c", "CCC");
c.put( "c'sMap", "csMapValue");
HashMap<String, Object> d = new HashMap<>();
d.put( "d", "DDD");
d.put( "d'sMap", "dsMapValue");
cm1.pushScope( a);
cm1.pushScope( b);
cm1.pushScope( c);
PrintStream o = System.out;
// we can make a chainMap part of another
ChainMap<String, Object> cmMeta = new ChainMap<>();
cmMeta.pushScope( cm1);
cmMeta.pushScope( d);
o.println( "__________");
for( Entry<String, Object> e: cmMeta.entrySet())
o.println( e.getKey() + ":" + e.getValue());
o.println( "__________");
/*Gives:
__________
d'sMap:dsMapValue
d:DDD
c:CCC
c'sMap:csMapValue
b:BBB
b'sMap:bsValue
a:A
a'sMap:asValue
__________
*/
}
public static void main( String[] args ) { examples1(); examples2(); }
}
由于这会模拟python ChainMap API,请注意aChainMapInstance.remove(someKey)
并不意味着密钥不会在那里。只有当scope-stack上的顶部映射包含密钥时,才会执行remove调用。
(更新为模仿dimo414的containsValue()
,hashcode()
和equals()
的实现。)
顺便提一下,在尝试构建这个时,我注意到有一个内容版本的两个地图与番石榴的隐式融合(如果这就是你需要的)。一个示例用例:两个映射都有数百万个密钥,您的用户可能根本不打算查询它。但是,与上面的ChainMap不同,修改将为您提供UnsupportedOperationException。这个应该通过嵌套来工作,例如:composedMapView(map1,composedMapView(map2,map3))。
public static <K, V>
Map<K, V> composedMapView( Map<K, V> look1st, Map<K, V> look2nd){
return Maps.asMap( Sets.union( look1st.keySet(), look2nd.keySet()),
k -> look1st.containsKey(k) ? look1st.get(k) : look2nd.get(k));
}