是否有一个集合集,集合可由其任何成员处理?

时间:2019-06-24 22:15:23

标签: java set

我正在尝试在Java(或Groovy)中找到一种类似这样的数据结构:

MemberAdressableSetsSet mass = new MemberAdressableSetsSet();
mass.addSet(["a","b"]);
mass.addSet(["c","d","e"]);
mass.get("d").add("f");
String output = Arrays.toString(mass.get("e").toArray());
System.out.println(output); // [ "c", "d", "e", "f" ] (ordering irrelevant)

是否存在类似的东西?如果没有,是否有办法用普通的Java代码来实现类似的东西,而这些代码却连续数周没有给CPU或内存带来噩梦?

编辑:更严格

MemberAdressableSetsSet mass = new MemberAdressableSetsSet();
Set<String> s1 = new HashSet<String>();
s1.add("a");
Set<String> s2 = new HashSet<String>();
s2.add("c");s2.add("d");s2.add("e");
mass.addSet(s1);
mass.addSet(s2);
Set<String> s3 = new HashSet<String>();
s3.add("a");s3.add("z");

mass.addSet(s3);
/* s3 contains "a", which is already in a subset of mass, so:
 * Either
 *   - does nothing and returns false or throws Exception
 *   - deletes "a" from its previous subset before adding s3
 *      => possibly returns the old subset
 *      => deletes the old subset if that leaves it empty
 *      => maybe requires an optional parameter to be set
 *   - removes "a" from the new subset before adding it
 *      => possibly returns the new subset that was actually added
 *      => does not add the new subset if purging it of overlap leaves it empty
 *      => maybe requires an optional parameter to be set
 *   - merges all sets that would end up overlapping
 *   - adds it with no overlap checks, but get("a") returns an array of all sets containing it
 */

mass.get("d").add("f");
String output = Arrays.toString(mass.get("e").toArray());
System.out.println(output); // [ "c", "d", "e", "f" ] (ordering irrelevant)

mass.get("d")将返回包含Set<T>的{​​{1}}中的mass。类似于get()的工作原理,例如"d"

HashMap

3 个答案:

答案 0 :(得分:0)

到目前为止我能想出的最好的方法是这样的:

HashMap<String,LinkedList<Integer>> map = new HashMap<>();
LinkedList<Integer> list = new LinkedList<>();
list.add(9);
map.put("d",list);
map.get("d").add(4);
map.get("d"); // returns a LinkedList with contents [9,4]

有一些缺点:

  • 不能直接访问集合,除非克隆是可接受的选项,否则所有import java.util.HashMap; import java.util.Set; public class MemberAdressableSetsSet { private int next_id = 1; private HashMap<Object,Integer> members = new HashMap(); private HashMap<Integer,Set> sets = new HashMap(); public boolean addSet(Set s) { if (s.size()==0) return false; for (Object member : s) { if (members.get(member)!=null) return false; } sets.put(next_id,s); for (Object member : s) { members.put(member,next_id); } next_id++; return true; } public boolean deleteSet(Object member) { Integer id = members.get(member); if (id==null) return false; Set set = sets.get(id); for (Object m : set) { members.remove(m); } sets.remove(id); return true; } public boolean addToSet(Object member, Object addition) { Integer id = members.get(member); if (id==null) throw new IndexOutOfBoundsException(); if (members.get(addition)!=null) return false; sets.get(id).add(addition); members.put(addition,id); return true; } public boolean removeFromSet(Object member) { Integer id = members.get(member); if (id==null) return false; Set s = sets.get(id); if (s.size()==1) sets.remove(id); else s.remove(member); members.remove(member); return true; } public Set getSetClone(Object member) { Integer id = members.get(member); if (id==null) throw new IndexOutOfBoundsException(); Set copy = new java.util.HashSet(sets.get(id)); return copy; } } 方法和属性都不会被显式定义的翻译方法公开,因此无法访问
  • 类型信息丢失。
    • 说添加了Set
      它不会抱怨尝试向该集合添加一个Set<Date>对象。

至少集合的丢失类型信息不会扩展到它们的成员:File仍能按预期工作,尽管在进行{{比较之前,双方都被强制转换为Set.contains() 1}}。因此,例如,当包含Object的集合被问到是否包含contains()时,它不会返回true,反之亦然。

  

包含(Object)3 的集合在被问到是否包含(Object)3L时将返回true(反之亦然),但是即使没有(Object)(new java.util.Date(10L))也是如此在前面,所以我想那是“按预期工作”¯\ _(ツ)_ /¯

答案 1 :(得分:0)

您需要多久一次访问一个元素?可能值得使用地图并在多个键下存储相同的Set参考。

我将防止对地图和子集的外部突变,并提供辅助方法来执行所有更新:

public class MemberAdressableSets<T> {
    Map<T, Set<T>> data = new HashMap<>();

    public void addSet(Set<T> dataSet) {
        if (dataSet.stream().anyMatch(data::containsKey)) {
            throw Exception("Key already in member addressable data");
        }
        Set<T> protectedSet = new HashSet<>(dataSet);
        dataSet.forEach(d -> data.put(d, protectedSet));
    }

    public void updateSet(T key, T... newData) {
        Set<T> dataSet = data.get(key);
        Arrays.stream(newData).forEach(dataSet::add);
        Arrays.stream(newData).forEach(d -> data.put(d, dataSet));
    }

    public Set<T> get(T key) {
        return Collections.unmodifiableSet(data.get(key));
    }
}

或者,如果密钥不存在,则可以更新addSetupdateSet以创建新的Set实例,并使updateSet从不throw。您还需要扩展此类以处理合并集的情况。即处理用例:

mass.addSet(["a","b"]);
mass.addSet(["a","c"]);

答案 2 :(得分:0)

该解决方案允许mass.get("d").add("f");之类的东西影响存储在mass中的子集,但是有很多缺点。

import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;

public class MemberAdressableSetsSetDirect {
    private LinkedHashSet<Set> sets;
    public void addSet(Set newSet) {
        sets.add(newSet);
    }
    public Set removeSet(Object member) {
        Iterator<Set> it = sets.iterator();
        while (it.hasNext()) {
            Set s = it.next();
            if (s.contains(member)) {
                it.remove();
                return s;
            }
        }
        return null;
    }
    public int removeSets(Object member) {
        int removed = 0;
        Iterator<Set> it = sets.iterator();
        while (it.hasNext()) {
            Set s = it.next();
            if (s.contains(member)) {
                it.remove();
                removed++;
            }
        }
        return removed;
    }
    public void deleteEmptySets() {
        sets.removeIf(Set::isEmpty);
    }
    public Set get(Object member) {
        for (Set s : sets) {
            if (s.contains(member)) return s;
        }
        return null;
    }
    public Set[] getAll(Object member) {
        LinkedHashSet<Set> results = new LinkedHashSet<>();
        for (Set s : sets) {
            if (s.contains(member)) results.add(s);
        }
        return (Set[]) results.toArray();
    }
}

没有内置的防止重叠的保护,因此我们使用的访问不可靠,并引入了可能需要定期手动清除deleteEmptySets()来清除无数空集的可能性,因为此解决方案可以无法检测子集是否被直接访问修改了。

MemberAdressableSetsSetDirect massd = new MemberAdressableSetsSetDirect();
Set s1 = new HashSet();Set s2 = new HashSet();Set s3 = new HashSet();
s1.add("a");s1.add("b");
s2.add("c");s2.add("d");
s3.add("e");
massd.addSet(s1);massd.addSet(s2);
massd.get("c").add("a");
// massd.get("a") will now either return the Set ["a","b"] or the Set ["a","c","d"]
// (could be that my usage of a LinkedHashSet as the basis of massd
//  at least makes it consistently return the set added first)
massd.get("e").remove("e");
// the third set is now empty, can't be accessed anymore,
// and massd has no clue about that until it's told to look for empty sets
massd.get("c").remove("d");
massd.get("c").remove("c");
// if LinkedHashSet makes this solution act as I suspected above,
// this makes the third subset inaccessible except via massd.getAll("a")[1]

此外,此解决方案也无法保留类型信息。
这甚至不会发出警告:

MemberAdressableSetsSetDirect massd = new MemberAdressableSetsSetDirect();
Set<Long> s = new HashSet<Long>();
s.add(3L);
massd.addSet(s);
massd.get(3L).add("someString");
// massd.get(3L) will now return a Set with contents [3L, "someString"]