32

重複の可能性があるオブジェクトのコレクションが与えられた場合、オブジェクトごとの出現回数を取得したいと思います。空の を初期化しMap、 を反復処理してCollection、オブジェクトをそのカウントにマッピングします (マップに既にオブジェクトが含まれるたびにカウントを増やします)。

public Map<Object, Integer> countOccurrences(Collection<Object> list) {
    Map<Object, Integer> occurrenceMap = new HashMap<Object, Integer>();
    for (Object obj : list) {
        Integer numOccurrence = occurrenceMap.get(obj);
        if (numOccurrence == null) {
            //first count
            occurrenceMap.put(obj, 1);
        } else {
            occurrenceMap.put(obj, numOccurrence++);
        }
    }
    return occurrenceMap;
}

これは、出現回数をカウントする単純なロジックとしては冗長すぎるように見えます。これを行うためのよりエレガントで短い方法はありますか? 私は、完全に異なるアルゴリズムまたはより短いコードを可能にするJava言語固有の機能を受け入れています。

4

12 に答える 12

20

Guava の Multisetを確認してください。まさにあなたが探しているものです。

残念ながら addAll(Iterable iterable) 関数はありませんが、 add(E e) を呼び出すコレクションの単純なループは簡単です。

編集

私の間違いですが、コレクションを実装しているため、実際には addAll メソッドがあります。

于 2013-01-10T14:36:10.207 に答える
20

それでは、Java 8 コードをいくつか試してみましょう。

static public Map<String, Integer> toMap(List<String> lst) {
    return lst.stream()
            .collect(HashMap<String, Integer>::new,
                    (map, str) -> {
                        if (!map.containsKey(str)) {
                            map.put(str, 1);
                        } else {
                            map.put(str, map.get(str) + 1);
                        }
                    },
                    HashMap<String, Integer>::putAll);
}
static public Map<String, Integer> toMap(List<String> lst) {
    return lst.stream().collect(Collectors.groupingBy(s -> s,
                                  Collectors.counting()));
}

このコードはよりエレガントだと思います。

于 2014-08-26T18:00:45.187 に答える
13

これは古い質問であることは承知していますが、Java 8 でこれらの投票をカウントするためのより洗練された方法を見つけました。気に入っていただければ幸いです。

Map<String, Long> map = a.getSomeStringList()
            .stream()
            .collect(Collectors.groupingBy(
                    Function.identity(),
                    Collectors.counting())
            );

エラーがあれば、コメントしてください。

于 2017-01-23T12:57:46.923 に答える
7

この記事を確認してくださいリスト内の要素の出現回数をカウントする方法。出現回数をカウントするには、 を使用できますint occurrences = Collections.frequency(list, obj);

于 2013-01-10T14:39:12.180 に答える
2

Java の場合はそれほど冗長ではありません。使用できますTObjectIntHashMap

public <T> TObjectIntHashMap<T> countOccurrences(Iterable<T> list) {
    TObjectIntHashMap<T> counts = new TObjectIntHashMap<T>();
    for (T obj : list) counts.adjustOrPut(obj, 1, 1);
    return counts;
}
于 2013-01-10T14:36:33.377 に答える
0

commons-collections:CollectionUtils.getCardinalityMapには、まさにこれを行うメソッドがあります。

于 2018-08-10T10:59:27.107 に答える
0

ここで@NimChimpskyとの議論への応答として、ソートされたコレクションを使用する、代替のより高速な-私が証明しようとしている-カウント方法があります。要素の数と「sortFactor」(コードを参照)によって速度の違いは異なりますが、実行環境(デバッグではない)の大量のオブジェクトの場合、私のメソッドはデフォルトの方法と比較して20〜30%の速度が向上します。以下は、両方のメソッドの簡単なテスト クラスです。

public class EltCountTest {

    final static int N_ELTS = 10000;

    static final class SampleCountedObject implements Comparable<SampleCountedObject>
    {
        int value = 0;

        public SampleCountedObject(int value) {
            super();
            this.value = value;
        }

        @Override
        public int compareTo(SampleCountedObject o) {
            return (value == o.value)? 0:(value > o.value)?1:-1; // just *a* sort
        }

        @Override
        public int hashCode() {
            return value;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj instanceof SampleCountedObject) {
                return value == ((SampleCountedObject)obj).value;
            }
            return false;
        }

        @Override
        public String toString() {
            return "SampleCountedObject("+value+")";
        }
    }

    /**
     * * @param args
     */
    public static void main(String[] args) {
        int tries = 10000;
        int sortFactor = 10;
        Map<SampleCountedObject, Integer> map1 = null;
        Map<SampleCountedObject, Integer> map2 = null;

        ArrayList<SampleCountedObject> objList = new ArrayList<EltCountTest.SampleCountedObject>(N_ELTS);

        for (int i =0, max=N_ELTS/sortFactor; i<max; i++){
            for (int j = 0; j<sortFactor; j++) {
                objList.add(new SampleCountedObject(i));
            }
        }

        long timestart = System.nanoTime();
        for (int a=0; a< tries; a++) {
            map1 = method1(objList);
        }
        System.out.println();
        long timeend1 = System.nanoTime();
        System.out.println();

        for (int a=0; a< tries; a++) {
            map2 = metod2(objList);
        }
        long timeend2 = System.nanoTime();
        System.out.println();


        long t1 = timeend1-timestart;
        long t2 = timeend2-timeend1;
        System.out.println("\n        org count method=["+t1+"]\nsorted collection method=["+t2+"]"+
                 "\ndiff=["+Math.abs(t1-t2)+"] percent=["+(100d*t2/t1)+"]");

        for (SampleCountedObject obj: objList) {
            int val1 = map1.get(obj);
            int val2 = map2.get(obj);
            if (val1 != val2) {
                throw new RuntimeException("val1 != val2 for obj "+obj);
            }
        }
        System.out.println("veryfy OK");

    }

    private static Map<SampleCountedObject, Integer> method1(ArrayList<SampleCountedObject> objList) {
        Map<SampleCountedObject, Integer> occurenceMap = new HashMap<SampleCountedObject, Integer>();

        for(SampleCountedObject obj: objList){
             Integer numOccurrence = occurenceMap.get(obj);
             if(numOccurrence == null){
                 occurenceMap.put(obj, 1);
             } else {
                 occurenceMap.put(obj, ++numOccurrence);
             }
        }
        return occurenceMap;
    }

    private static Map<SampleCountedObject, Integer> metod2(ArrayList<SampleCountedObject> objList) {
        Map<SampleCountedObject, Integer> occurenceMap = new HashMap<SampleCountedObject, Integer>();
        int count = 0;
        Collections.sort(objList);
        SampleCountedObject prevObj = objList.get(0);

        for(SampleCountedObject obj: objList){
            if (!obj.equals(prevObj)) {
                occurenceMap.put(prevObj, count);
                count = 1;
            } else {
                count ++;
            }
            prevObj = obj;
        }
        occurenceMap.put(prevObj, count);
        return occurenceMap;
    }
}

結果が同じであることも確認し、テスト結果を印刷した後に行うことに注意してください。

私が興味深いと思ったのは、デバッグ実行で私のメソッドが元のメソッドよりもかなり遅いことです (これも、コレクション内の要素の数に応じて 10-20%)。

于 2013-01-10T15:27:00.390 に答える
-2

Javaは冗長な言語です。サードパーティのライブラリを使用するか、Java 8のLambda式を待たない限り、これを実現するためのより簡単な方法はないと思います。

于 2013-01-10T14:37:51.787 に答える