Java在HashSet中找到最常见的值

时间:2011-10-04 10:58:51

标签: java java-ee hashset

这里有一个基本问题,但我想知道最好的方法是什么......

我有一个HashSet,我正在添加对象,.add()方法只会添加一个对象(如果它尚不存在)。但我想要做的是添加所有对象,然后在最后获得以下结果..

- 独特(不同)对象的数量
- 物体的平均频率

有人能指出我正确的方向吗?

提前致谢

6 个答案:

答案 0 :(得分:7)

使用HashMap。使用条目作为键,并将它们映射到整数以保持计数。

编辑:你可能想要包装HashMap,以确保每次添加或删除对象时,都会适当地修改计数器。
为了帮助您入门:

class MapWrapper<Key>
{
    private Map<Key,Integer> map = new HashMap<Key, Integer>();

    void add( Key key )
    {
        Integer n = map.get( key );
        if ( n == null )
        {
            map.put( key, 1 );
        }
        else
        {
            map.put( key, new Integer( n + 1 ));
        }
    }

    int occurrences( Key k )
    {
        Integer n = map.get( k );
        if ( n == null )
        {
            return 0;
        }
        else
        {
            return n;
        }
    }
}

答案 1 :(得分:2)

HashSet并不适合跟踪个人数量,但HashMap几乎是完美的。

import java.util.HashMap;
import java.util.Map;

public class Count<K, V> extends HashMap<K, V> {

    // Counts unique objects
    public void add(K o) {
        int count = this.containsKey(o) ? ((Integer)this.get(o)).intValue() + 1 : 1;
        super.put(o, (V) new Integer(count));
    }

    // Demonstration
    public static void main(String[] args) {

        Count<Object, Integer> c = new Count<Object, Integer>();

        String one = "one";
        String two = "two";
        String six = "six";

        c.add(one);
        c.add(two);
        c.add(two);
        c.add(six);
        c.add(six);
        c.add(six);

        System.out.println("Number of distinct objects: " + c.size());

        System.out.println("Frequency of different objects: ");

        for (Map.Entry<Object, Integer> entry : c.entrySet()) {
            System.out.println(entry.getKey() + " - " + entry.getValue());
        }
    }
}

运行时,此独立代码段输出

Number of distinct objects - 3
Frequency of different objects:
two - 2
one - 1
six - 3

答案 2 :(得分:1)

不同对象的数量只是之后哈希集的大小。

根据“平均频率”的含义,您可以使用source.size() / set.size() ...(如果需要,可以将其中一个操作数强制转换为double以强制浮点运算)。如果您可以通过一些示例详细说明您的需求,我们可以提供更多帮助。

答案 3 :(得分:1)

番石榴HashMultiset是一个方便的选择。例如:

HashMultiset<String> multiSet = HashMultiset.create();
multiSet.add("a");
multiSet.add("a");
multiSet.add("b");

Assert.assertEquals(2, multiSet.count("a"));//count "a" 
Assert.assertEquals(3, multiSet.size());//set size
Assert.assertEquals(2, multiSet.elementSet().size());//unique (distinct) size 

答案 4 :(得分:0)

您可以只使用(哈希)地图而不是将每个不同对象的计数保留为地图中的值,或者您可以继续使用集合但在某处计算所有要添加的调用。

插入的对象总数是您计算的数量或地图中所有值的总和(迭代EntrySet)。不同对象的数量始终是地图/集的大小()和平均值。频率显然是商。

答案 5 :(得分:0)

对于这种情况,我使用自己的Map接口实现:

/*
* Providers easily work with maps of lists
* */
public interface ManyValuedMap<K, V> extends Cloneable, Map<K, List<V>>, Serializable{

    public List<V> put(K key, V... values);
    public void clear(K key);
    public ManyValuedMap<K, V> clone();
    public void sort(Comparator<? super V> c);
    public List<V> getAllValues();
    public Collection<List<V>> values(Comparator<? super K> c);
    public void lock();
    public Map<K, List<V>> toMap();

}

并实施:

/**
 * in ManyValuedMap can be stored lists of elements identificated by some key
 * */
public class ManyValuedHashMap<K, V> implements ManyValuedMap<K, V>, Serializable {

    //linked hash map guarantees right key order
    private Map<K, List<V>> map = new LinkedHashMap<K, List<V>>();
    private boolean isNeedToCheckUniqueness;
    private boolean lock = false;

    /**
     * @param needToCheckUniqueness if true then every time when element added uniqueness will be checked
     * */
    public ManyValuedHashMap(boolean needToCheckUniqueness) {
        isNeedToCheckUniqueness = needToCheckUniqueness;
    }

    public ManyValuedHashMap() {
        this(false);
    }

    public ManyValuedHashMap<K, V> put2 (K key, List<V> newValues ) {
        put(key, newValues);
        return this;
    }

    public List<V> put ( K key, List<V> newValues ) {
        if ( newValues == null ) {
            return put(key, (V)null);
        } else if ( newValues.isEmpty() ) {
            return put(key, (V)null);
        } else {
            //noinspection unchecked
            return put(key, (V[])newValues.toArray() );
        }
    }

    public List<V> put(K key, V... newValues) {
        checkLock();
        List<V> curValues = null;
        if (newValues != null && key != null) {
            curValues = this.map.get(key);

            if (curValues == null) {
                //new values  - add
                curValues = new ArrayList<V>();
                curValues.addAll(Arrays.asList(newValues));
                this.map.put(key, curValues);
            } else {
                // for this key values were added
                if (isNeedToCheckUniqueness) {
                    //if is need to check uniqueness - check
                    Integer index;
                    for (V newValue : newValues) {
                        index = null;
                        for (int i = 0; i < curValues.size(); i++) {
                            if (curValues.get(i).equals(newValue)) {
                                index = i;
                                break;
                            }
                        }
                        if (index == null) {
                            curValues.add(newValue);
                        } /*else {
                            //no need to add
                            //this value is already stored in map
                        }*/
                    }
                } else {
                    //otherwise add
                    curValues.addAll(Arrays.asList(newValues));
                }
            }
        } else if (key != null) {
            curValues = this.map.get(key);

            if (curValues == null) {
                curValues = new ArrayList<V>();
                this.map.put(key, curValues);
            }
        }

        return curValues;
    }

    public boolean containsValue(Object value) {
        boolean result = false;
        for (List<V> values : this.map.values()) {
            for (V v : values) {
                if (v.equals(value)) {
                    result = true;
                    break;
                }
            }
            if (result) {
                break;
            }
        }
        return result;
    }

    public List<V> get(Object key) {
        return this.map.get(key);
    }

    public boolean containsKey(Object key) {
        return this.map.containsKey(key);
    }

    public boolean isEmpty() {
        return this.map.isEmpty();
    }

    public int size() {
        int size = 0;
        for (List<V> vs : map.values()) {
            size += vs.size();
        }
        return size;
    }

    public List<V> remove(Object key) {
        checkLock();
        return this.map.remove(key);
    }

    @Override
    public void putAll(Map<? extends K, ? extends List<V>> m) {
        checkLock();
        this.map.putAll(m);
    }

    public void clear() {
        checkLock();
        this.map.clear();
    }

    @Override
    public void clear(K key) {
        checkLock();
        List<V> curValues = this.map.get(key);
        if ( curValues != null ) {
            curValues.clear();
        }
    }

    public Set<K> keySet() {
        return this.map.keySet();
    }

    public Collection<List<V>> values() {
        return this.map.values();
    }

    public Set<Map.Entry<K, List<V>>> entrySet() {
        return this.map.entrySet();
    }

    public Map<K, List<V>> toMap() {
        return new HashMap<K, List<V>>(map);
    }

    @Override
    public ManyValuedHashMap<K, V> clone() {
        ManyValuedHashMap<K, V> clone = null;
        try {
            //noinspection unchecked
            clone = (ManyValuedHashMap<K, V>)super.clone();
            //IMPORTANT: NOT DEEP CLONE
            //noinspection unchecked
            clone.map = new LinkedHashMap<K, List<V>>();
            clone.map.putAll(this.map);
        } catch (CloneNotSupportedException e) {
            Logger.getLogger(this.getClass()).error(e.getMessage(), e);
        }
        return clone;
    }

    @Override
    public void sort(Comparator<? super V> c) {
        for (List<V> list : map.values()) {
            Collections.sort(list, c);
        }
    }

    @Override
    public List<V> getAllValues() {
        final List<V> result = new ArrayList<V>();
        for (List<V> list : map.values()) {
            result.addAll(list);
        }
        return result;
    }

    public Collection<List<V>> values(Comparator<? super K> c) {
        List<Map.Entry<K, List<V>>> entries = new ArrayList<Map.Entry<K, List<V>>>(entrySet());

        Collections.sort(entries, new EntryComparator(c));

        Collection<List<V>> result = new ArrayList<List<V>>();

        for (Map.Entry<K, List<V>> entry : entries) {
            result.add(entry.getValue());
        }

        return result;
    }

    private class EntryComparator implements Comparator<Map.Entry<K, List<V>>>{

        private Comparator<? super K> keyComparator = null;

        private EntryComparator(Comparator<? super K> keyComparator) {
            this.keyComparator = keyComparator;
        }

        @Override
        public int compare(Map.Entry<K, List<V>> o1, Map.Entry<K, List<V>> o2) {
            return keyComparator.compare(o1.getKey(), o2.getKey());
        }
    }

    @Override
    public void lock() {
        this.lock = true;
    }

    private void checkLock () {
        if ( this.lock ) {
            throw new UnsupportedOperationException();
        }
    }
}

接下来是行为:

  1. 列表项
  2. 如果您尝试使用未在地图中显示的键添加值,则将创建新的List元素并将其存储在具有指定键的地图中(值将添加到课程列表中)
  3. 如果key已经在map中,那么:if isNeedToCheckUniqueness == false新值将被添加到列表末尾,否则(isNeedToCheckUniqueness == true)值将被添加到列表中,以防列表尚未列出包含它。
  4. 您可以通过获取列表大小轻松按键(频率)计算元素数量。 您可以获取列表的第一个或最后一个元素,以获得具有指定键的第一个或最后一个添加值。