Skip to content

Instantly share code, notes, and snippets.

@froop
Last active August 29, 2015 13:58
Show Gist options
  • Save froop/10001048 to your computer and use it in GitHub Desktop.
Save froop/10001048 to your computer and use it in GitHub Desktop.
[Java] 同一Key単位にValueをCollection化したMap
/**
* 同一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();
}
}
/**
* グラフのノード群のMap.
* @param <T> Type: ノードの型
*/
public interface GraphMap<T> extends Map<T, Set<T>> {
}
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));
}
}
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));
}
}
/**
* グラフノード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);
}
}
/**
* 同一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>();
}
}
/**
* 同一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);
}
}
@froop
Copy link
Author

froop commented Apr 9, 2014

Google Guava の Multimap を使おう(提案)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment