Last active
August 29, 2015 13:58
-
-
Save froop/10001048 to your computer and use it in GitHub Desktop.
[Java] 同一Key単位にValueをCollection化したMap
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 同一Key単位にValueをCollection化したMapを生成. | |
* @param <K> Key: キーの型 | |
* @param <V> Value: 値の型 | |
* @param <C> Collection: 値集合の型 | |
*/ | |
public abstract class CollectionMapBuilder<K, V, C extends Collection<V>> { | |
private final Map<K, C> map = new LinkedHashMap<K, C>(); | |
/** | |
* 要素を追加. | |
*/ | |
public void add(K key, V value) { | |
if (key == null) { | |
return; | |
} | |
C values = map.get(key); | |
if (values == null) { | |
values = createCollection(); | |
} | |
if (value != null) { | |
values.add(value); | |
} | |
map.put(key, values); | |
} | |
/** | |
* 要素を追加(keyのみ). | |
*/ | |
public void add(K key) { | |
if (map.get(key) == null) { | |
map.put(key, createCollection()); | |
} | |
} | |
/** | |
* 空のコレクションを生成. | |
*/ | |
protected abstract C createCollection(); | |
/** | |
* @return 結果のMap | |
*/ | |
public Map<K, C> build() { | |
return map; | |
} | |
/** | |
* KeyとValueを反転. | |
* @param <K> Key: キーの型 | |
* @param <V> Value: 値の型 | |
* @param <C> Collection: 値集合の型 | |
* @param <R> Result: 変換後の値集合の型 | |
*/ | |
public static <K, V, C extends Collection<V>, R extends Collection<K>> | |
Map<V, R> invert(CollectionMapBuilder<V, K, R> builder, Map<K, C> map) { | |
for (Map.Entry<K, C> item : map.entrySet()) { | |
K key = item.getKey(); | |
C values = item.getValue(); | |
if (values.isEmpty()) { | |
builder.add(null, key); | |
} else { | |
for (V value : values) { | |
builder.add(value, key); | |
} | |
} | |
} | |
return builder.build(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* グラフのノード群のMap. | |
* @param <T> Type: ノードの型 | |
*/ | |
public interface GraphMap<T> extends Map<T, Set<T>> { | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package jobsche.model.util.collection.multi; | |
/** | |
* グラフノードのMapを生成. | |
* @param <T> Type: ノードの型 | |
*/ | |
public class GraphMapBuilder<T> extends SetMapBuilder<T, T> { | |
@Override | |
public void add(T key, T value) { | |
super.add(key, value); | |
add(value); | |
} | |
@Override | |
public GraphMap<T> build() { | |
return new GraphMapImpl<T>(super.build()); | |
} | |
/** | |
* グラフの方向を反転. | |
*/ | |
public static <T> GraphMap<T> invert(GraphMap<T> map) { | |
return new GraphMapImpl<T>(SetMapBuilder.invert(new GraphMapBuilder<T>(), map)); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class GraphMapBuilderTest { | |
private GraphMapBuilder<String> target; | |
@Before | |
public void setUp() { | |
target = new GraphMapBuilder<String>(); | |
target.add("node1"); | |
target.add("node2", "node3"); | |
target.add("node3", "node1"); | |
target.add("node3", "node5"); | |
target.add("node4", "node5"); | |
} | |
@Test | |
public void testBuild() { | |
GraphMap<String> res = target.build(); | |
Iterator<Entry<String, Set<String>>> it = res.entrySet().iterator(); | |
assertNode(it.next(), "node1", Collections.<String>emptySet()); | |
assertNode(it.next(), "node2", asSet("node3")); | |
assertNode(it.next(), "node3", asSet("node1", "node5")); | |
assertNode(it.next(), "node5", Collections.<String>emptySet()); | |
assertNode(it.next(), "node4", asSet("node5")); | |
assertFalse(it.hasNext()); | |
} | |
@Test | |
public void testBuild_Single() { | |
target = new GraphMapBuilder<String>(); | |
target.add("node1"); | |
GraphMap<String> res = target.build(); | |
Iterator<Entry<String, Set<String>>> it = res.entrySet().iterator(); | |
assertNode(it.next(), "node1", Collections.<String>emptySet()); | |
assertFalse(it.hasNext()); | |
} | |
@Test | |
public void testBuild_Empty() { | |
target = new GraphMapBuilder<String>(); | |
GraphMap<String> res = target.build(); | |
Iterator<Entry<String, Set<String>>> it = res.entrySet().iterator(); | |
assertFalse(it.hasNext()); | |
} | |
@Test | |
public void testInvert() { | |
GraphMap<String> res = GraphMapBuilder.invert(target.build()); | |
Iterator<Entry<String, Set<String>>> it = res.entrySet().iterator(); | |
assertNode(it.next(), "node1", asSet("node3")); | |
assertNode(it.next(), "node3", asSet("node2")); | |
assertNode(it.next(), "node2", Collections.<String>emptySet()); | |
assertNode(it.next(), "node5", asSet("node3", "node4")); | |
assertNode(it.next(), "node4", Collections.<String>emptySet()); | |
assertFalse(it.hasNext()); | |
} | |
@Test | |
public void testInvert_Single() { | |
target = new GraphMapBuilder<String>(); | |
target.add("node1"); | |
GraphMap<String> res = GraphMapBuilder.invert(target.build()); | |
Iterator<Entry<String, Set<String>>> it = res.entrySet().iterator(); | |
assertNode(it.next(), "node1", Collections.<String>emptySet()); | |
assertFalse(it.hasNext()); | |
} | |
@Test | |
public void testInvert_Empty() { | |
target = new GraphMapBuilder<String>(); | |
GraphMap<String> res = GraphMapBuilder.invert(target.build()); | |
Iterator<Entry<String, Set<String>>> it = res.entrySet().iterator(); | |
assertFalse(it.hasNext()); | |
} | |
@Test | |
public void testInvert_loop1() { | |
GraphMapBuilder<String> target = new GraphMapBuilder<String>(); | |
target.add("node1", "node1"); | |
GraphMap<String> res = GraphMapBuilder.invert(target.build()); | |
Iterator<Entry<String, Set<String>>> it = res.entrySet().iterator(); | |
assertNode(it.next(), "node1", asSet("node1")); | |
assertFalse(it.hasNext()); | |
} | |
@Test | |
public void testInvert_loop2() { | |
GraphMapBuilder<String> target = new GraphMapBuilder<String>(); | |
target.add("node1", "node2"); | |
target.add("node2", "node1"); | |
GraphMap<String> res = GraphMapBuilder.invert(target.build()); | |
Iterator<Entry<String, Set<String>>> it = res.entrySet().iterator(); | |
assertNode(it.next(), "node2", asSet("node1")); | |
assertNode(it.next(), "node1", asSet("node2")); | |
assertFalse(it.hasNext()); | |
} | |
private static void assertNode(Entry<String, Set<String>> actual, String key, | |
Set<String> value) { | |
assertThat("key", actual.getKey(), is(key)); | |
assertThat("value", actual.getValue(), is(value)); | |
} | |
private static <T> Set<T> asSet(T... items) { | |
return new LinkedHashSet<T>(Arrays.asList(items)); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* グラフノードMapの実装. | |
* @param <T> Type: ノードの型 | |
*/ | |
public class GraphMapImpl<T> extends LinkedHashMap<T, Set<T>> implements GraphMap<T> { | |
private static final long serialVersionUID = 1L; | |
public GraphMapImpl() { | |
super(); | |
} | |
public GraphMapImpl(Map<T, Set<T>> source) { | |
super(source); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 同一Key単位にValueをList化したMapを生成. | |
* @param <K> Key: キーの型 | |
* @param <V> Value: 値の型 | |
*/ | |
public class ListMapBuilder<K, V> | |
extends CollectionMapBuilder<K, V, List<V>> { | |
@Override | |
protected List<V> createCollection() { | |
return new ArrayList<V>(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 同一Key単位にValueをSet化したMapを生成. | |
* @param <K> Key: キーの型 | |
* @param <V> Value: 値の型 | |
*/ | |
public class SetMapBuilder<K, V> | |
extends CollectionMapBuilder<K, V, Set<V>> { | |
@Override | |
protected Set<V> createCollection() { | |
return new LinkedHashSet<V>(); | |
} | |
/** | |
* KeyとValueを反転. | |
* @param <K> Key: キーの型 | |
* @param <V> Value: 値の型 | |
*/ | |
public static <K, V> Map<V, Set<K>> invert( | |
SetMapBuilder<V, K> builder, Map<K, Set<V>> map) { | |
return CollectionMapBuilder.invert(builder, map); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Google Guava の Multimap を使おう(提案)