リスト内の要素の出現回数を数える方法


173

私が持ってArrayList次のように、Javaのコレクションクラスを:

ArrayList<String> animals = new ArrayList<String>();
animals.add("bat");
animals.add("owl");
animals.add("bat");
animals.add("bat");

ご覧のとおり、animals ArrayListは3つのbat要素と1つのowl要素で構成されています。コレクションフレームワークに、bat発生数を返すAPIがあるのか​​、それとも発生数を判別する別の方法があるのか​​と思いました。

GoogleのコレクションにMultisetは、要素の出現回数の合計を返すAPIがあることがわかりました。ただし、これはJDK 1.5とのみ互換性があります。弊社の製品は現在JDK 1.6ですので使用できません。


これが、実装ではなくインターフェイスにプログラムする必要がある理由の1つです。適切なコレクションを見つけた場合は、そのコレクションを使用するようにタイプを変更する必要があります。これについて回答を掲載します。
OscarRyz、2009

回答:


333

コレクションの静的周波数メソッドがここで役に立つと確信しています:

int occurrences = Collections.frequency(animals, "bat");

それはとにかくそれを私がやる方法です。私はこれがjdk 1.6であると確信しています。


プロジェクトに別の依存関係を追加するJREのAPIを常に優先します。そして、車輪を再発明しないでください!!
フェルナンド。

(それは問題ではないので、誰もがその前のバージョンを使用していませんが)それは、JDK 5で導入されましたdocs.oracle.com/javase/8/docs/technotes/guides/collections/...
ミニオンジム

105

Java 8の場合:

Map<String, Long> counts =
    list.stream().collect(Collectors.groupingBy(e -> e, Collectors.counting()));

6
e-> eの代わりにFunction.identity()(静的インポートを使用)を使用すると、読みやすくなります。
Kuchi

8
なぜこれよりも優れているのCollections.frequency()ですか?読みにくいようです。
rozina

これは要求されたものではありません。必要以上に機能します。
Alex Worden 2017年

8
これは要求された以上のことをするかもしれませんが、それは私が望んだことを正確に実行します(リスト内の個別の要素のマップをそれらのカウントに取得します)。さらに、この質問は私が検索したときにグーグルでトップの結果でした。
KJP、2017年

@rozina 1回のパスですべてのカウントを取得します。
atoMerz

22

これは、Effective Java bookで説明されているように、「オブジェクトをインターフェースで参照する」ことが重要である理由を示しています

実装にコードを記述し、たとえば、コード内の50か所でArrayListを使用する場合、アイテムをカウントする適切な "List"実装を見つけたら、それらの50か所すべてを変更する必要があります。おそらく、あなたのコードを壊す(あなただけがそれを使っているなら大したことはありませんが、それが他の誰かが使っているならあなたも彼らのコードを壊すでしょう)

インターフェイスにプログラミングすることで、これらの50の場所を変更せずに、実装をArrayListから "CountItemsList"(たとえば)または他のクラスに置き換えることができます。

以下は、これをどのように書くことができるかに関する非常に基本的なサンプルです。これは単なるサンプルであり、プロダクションレディリストははるかに複雑になります。

import java.util.*;

public class CountItemsList<E> extends ArrayList<E> { 

    // This is private. It is not visible from outside.
    private Map<E,Integer> count = new HashMap<E,Integer>();

    // There are several entry points to this class
    // this is just to show one of them.
    public boolean add( E element  ) { 
        if( !count.containsKey( element ) ){
            count.put( element, 1 );
        } else { 
            count.put( element, count.get( element ) + 1 );
        }
        return super.add( element );
    }

    // This method belongs to CountItemList interface ( or class ) 
    // to used you have to cast.
    public int getCount( E element ) { 
        if( ! count.containsKey( element ) ) {
            return 0;
        }
        return count.get( element );
    }

    public static void main( String [] args ) { 
        List<String> animals = new CountItemsList<String>();
        animals.add("bat");
        animals.add("owl");
        animals.add("bat");
        animals.add("bat");

        System.out.println( (( CountItemsList<String> )animals).getCount( "bat" ));
    }
}

ここで適用されるオブジェクト指向の原則:継承、ポリモーフィズム、抽象化、カプセル化。


12
まあ、継承よりも常に合成を試すべきです。LinkedListなどが必要な場合に、実装がArrayListにスタックします。あなたの例では、コンストラクタ/ファクトリで別のLIstを受け取り、ラッパーを返しているはずです。
mP。

まったく同感です。サンプルで継承を使用した理由は、構成を使用するよりも継承を使用して実行例を示す方がはるかに簡単だからです(Listインターフェースを実装する必要がある)。継承は最高の結合を作成します。
OscarRyz、2009

2
しかし、CountItemsListという名前を付けると、2つのことを実行し、項目をカウントし、リストになります。そのクラスの1つの責任(発生を数える)は単純で、Listインターフェースを実装する必要はないと思います。
flob

11

申し訳ありませんが、それを実行できる単純なメソッド呼び出しはありません。ただし、マップを作成して頻度をカウントするだけです。

HashMap<String,int> frequencymap = new HashMap<String,int>();
foreach(String a in animals) {
  if(frequencymap.containsKey(a)) {
    frequencymap.put(a, frequencymap.get(a)+1);
  }
  else{ frequencymap.put(a, 1); }
}

これは実際にはスケーラブルなソリューションではありません。MMのデータセットに数百、数千のエントリがあり、MMがすべてのエントリの頻度を知りたいと考えたとします。これは非常にコストのかかる作業になる可能性があります。特に、より優れた方法がある場合はなおさらです。
mP。

はい、それは良い解決策ではないかもしれませんが、それが間違っているという意味ではありません。
Adeel Ansari、

1
@dehmann、私は彼が文字通り4要素のコレクションでのコウモリの出現回数を望んでいるとは思わない、私はそれが単なるサンプルデータだったので、よりよく理解できると思う:-)。
paxdiablo 2009

2
@酢2/2。プログラミングとは今や物事を適切に行うことなので、将来ユーザーや別のコーダーであっても、他の誰かに頭痛や不快な経験をさせることはありません。PS:コードが多ければ多いほど、問題が発生する可能性が高くなります。
mP。

2
@mP:これがスケーラブルなソリューションではない理由を説明してください。Ray Hidayatは、各トークンを検索できるように、各トークンの頻度カウントを作成しています。より良い解決策は何ですか?
stackoverflowuser2010 2013年

10

これを行うためのJavaのネイティブメソッドはありません。ただし、Apache Commons-CollectionsのIterableUtils#countMatches()を使用して実行できます。


以下の私の答えを参照してください-正しい答えは、クエリが行われるたびにエントリを最初から最後までカウントするのではなく、最初からカウントのアイデアをサポートする構造を使用することです。
mP。

@mPでは、あなたとは異なる意見を持つすべての人に反対票を投じるだけですか?なんらかの理由でバッグを使用できない場合、またはネイティブコレクションのいずれかを使用できない場合はどうなりますか?
ケビン

-1は痛い敗者であることを意味します:-)私はmPがあなたに結果を求めたときはいつも時間がかかるため、mPがあなたに反対票を投じたと思います。バッグは挿入時だけ少し時間がかかります。データベースと同様に、これらの種類の構造は「書き込みよりも読み取りが多い」傾向があるため、低コストのオプションを使用することは理にかなっています。
paxdiablo 2009

そして、あなたの答えはネイティブでないものも必要とするようですので、あなたのコメントは少し奇妙に見えます。
paxdiablo 2009

皆さん、ありがとうございました。2つのアプローチのうちの1つまたは両方が機能すると思います。明日やってみます。
MM。

9

実際、Collectionsクラスには、次の静的メソッドがある:frequency(コレクションc、オブジェクトo)は、検索している要素の出現回数を返します。ちなみに、これは完全に機能します。

ArrayList<String> animals = new ArrayList<String>();
animals.add("bat");
animals.add("owl");
animals.add("bat");
animals.add("bat");
System.out.println("Freq of bat: "+Collections.frequency(animals, "bat"));

27
Lars Andrenはあなたの5年前に同じ答えを投稿しました。
Fabian Barney、

9

Streamsを使用した代替Java 8ソリューション:

long count = animals.stream().filter(animal -> "bat".equals(animal)).count();

8

なぜGoogleのCollection APIをJDK 1.6で使用できないのでしょうか。そうですか?それは下位バージョン用に構築されているため、互換性の問題はないはずです。1.6用にビルドされており、1.5を実行している場合は、ケースは異なります。

どこか間違ってる?


彼らはAPIをjdk 1.6にアップグレードしている最中であることを明確に述べています。
MM。

1
それは古い互換性を作ることはありません。そうですか?
Adeel Ansari、

すべきではない。しかし、彼らは免責事項を投げた方法は、彼らの0.9バージョンでそれを使用する私は不快になります
MM。

1.6で使用しています。1.5とのみ互換性があるとどこに記載されていますか?
Patrick

2
「1.6へのアップグレード」とは、「1.6との互換性の修正」ではなく、「1.6の新しい機能を利用するためのアップグレード」を意味します。
アダムJaskiewicz

6

もう少し効率的なアプローチは

Map<String, AtomicInteger> instances = new HashMap<String, AtomicInteger>();

void add(String name) {
     AtomicInteger value = instances.get(name);
     if (value == null) 
        instances.put(name, new AtomicInteger(1));
     else
        value.incrementAndGet();
}

6

リストからオブジェクトのオカレンスを直接取得するには:

int noOfOccurs = Collections.frequency(animals, "bat");

リスト内のObjectコレクションの出現を取得するには、Objectクラスのequalsメソッドを次のようにオーバーライドします。

@Override
public boolean equals(Object o){
    Animals e;
    if(!(o instanceof Animals)){
        return false;
    }else{
        e=(Animals)o;
        if(this.type==e.type()){
            return true;
        }
    }
    return false;
}

Animals(int type){
    this.type = type;
}

次のようにCollections.frequencyを呼び出します。

int noOfOccurs = Collections.frequency(animals, new Animals(1));

6

Java 8機能を使用して配列内の文字列値の出現を見つける簡単な方法。

public void checkDuplicateOccurance() {
        List<String> duplicateList = new ArrayList<String>();
        duplicateList.add("Cat");
        duplicateList.add("Dog");
        duplicateList.add("Cat");
        duplicateList.add("cow");
        duplicateList.add("Cow");
        duplicateList.add("Goat");          
        Map<String, Long> couterMap = duplicateList.stream().collect(Collectors.groupingBy(e -> e.toString(),Collectors.counting()));
        System.out.println(couterMap);
    }

出力:{Cat = 2、Goat = 1、Cow = 1、cow = 1、Dog = 1}

「Cow」とcowは同じ文字列とは見なされないことに気づくでしょう。同じカウントで必要な場合は、.toLowerCase()を使用してください。同じことを以下のスニペットで見つけてください。

Map<String, Long> couterMap = duplicateList.stream().collect(Collectors.groupingBy(e -> e.toString().toLowerCase(),Collectors.counting()));

出力:{cat = 2、cow = 2、goat = 1、dog = 1}


nit:リストは文字列のリストであるため、必要toString()ありません。あなただけ行うことができます:duplicateList.stream().collect(Collectors.groupingBy(e -> e,Collectors.counting()));
タッド

5

あなたが欲しいのはバッグです-これはセットのようなものですが、発生回数も数えます。残念ながら、Javaコレクションフレームワーク-Bag実装がないので素晴らしいです。そのため、Apache Common Collection リンクテキストを使用する必要があります


1
最高のスケーラブルなソリューション。サードパーティのものを使用できない場合は、独自のものを作成してください。バッグは、作成するロケット科学ではありません。+1。
paxdiablo 2009

他の人が頻度カウントデータ構造の実装を提供している間、いくつかのあいまいな答えを与えることに反対票を投じました。リンクした「bag」データ構造も、OPの質問に対する適切な解決策ではありません。この「バッグ」構造は、トークンの特定の数のコピーを保持することを目的としており、トークンの出現回数を数えることを目的としていません。
stackoverflowuser2010

2
List<String> list = Arrays.asList("as", "asda", "asd", "urff", "dfkjds", "hfad", "asd", "qadasd", "as", "asda",
        "asd", "urff", "dfkjds", "hfad", "asd", "qadasd" + "as", "asda", "asd", "urff", "dfkjds", "hfad", "asd",
        "qadasd", "as", "asda", "asd", "urff", "dfkjds", "hfad", "asd", "qadasd");

方法1:

Set<String> set = new LinkedHashSet<>();
set.addAll(list);

for (String s : set) {

    System.out.println(s + " : " + Collections.frequency(list, s));
}

方法2:

int count = 1;
Map<String, Integer> map = new HashMap<>();
Set<String> set1 = new LinkedHashSet<>();
for (String s : list) {
    if (!set1.add(s)) {
        count = map.get(s) + 1;
    }
    map.put(s, count);
    count = 1;

}
System.out.println(map);

Stack Overflowへようこそ!他の人がソリューションを理解しやすくするために、コードの説明を検討してください。
アンチモン2017

2

Eclipseコレクションを使用している場合は、Bag。AはMutableBag、任意の実装から返される可能性がRichIterable呼び出すことによってtoBag()

MutableList<String> animals = Lists.mutable.with("bat", "owl", "bat", "bat");
MutableBag<String> bag = animals.toBag();
Assert.assertEquals(3, bag.occurrencesOf("bat"));
Assert.assertEquals(1, bag.occurrencesOf("owl"));

HashBagEclipseコレクションの実装は、MutableObjectIntMap

注:私はEclipseコレクションのコミッターです。


1

arrayListの要素をhashMapに入れて、頻度をカウントします。


これは、tweaktがコードサンプルで言うのとまったく同じです。
mP。

1

Java 8-別の方法

String searched = "bat";
long n = IntStream.range(0, animals.size())
            .filter(i -> searched.equals(animals.get(i)))
            .count();

0

だから昔ながらの方法でそれを行い、あなた自身のものを転がしてください:

Map<String, Integer> instances = new HashMap<String, Integer>();

void add(String name) {
     Integer value = instances.get(name);
     if (value == null) {
        value = new Integer(0);
        instances.put(name, value);
     }
     instances.put(name, value++);
}

競合状態を回避するために、必要に応じて適切な「同期」を使用します。しかし、私はこれを独自のクラスで見たいと思っています。
paxdiablo 2009

あなたはタイプミスを持っています。マップで使用するため、代わりにHashMapが必要です。しかし、1の代わりに0を置くという間違いは、もう少し深刻です。
Adeel Ansari、

0

あなたが私のForEach DSLのユーザーであれば、それはCountクエリで行うことができます。

Count<String> query = Count.from(list);
for (Count<Foo> each: query) each.yield = "bat".equals(each.element);
int number = query.result();

0

私はこのケースをもっと難しくしたくはなく、LastName-> FirstNameのHashMapを2つのイテレータで作成しました。そして、私のメソッドは重複したFirstNameを持つ項目を削除する必要があります。

public static void removeTheFirstNameDuplicates(HashMap<String, String> map)
{

    Iterator<Map.Entry<String, String>> iter = map.entrySet().iterator();
    Iterator<Map.Entry<String, String>> iter2 = map.entrySet().iterator();
    while(iter.hasNext())
    {
        Map.Entry<String, String> pair = iter.next();
        String name = pair.getValue();
        int i = 0;

        while(iter2.hasNext())
        {

            Map.Entry<String, String> nextPair = iter2.next();
            if (nextPair.getValue().equals(name))
                i++;
        }

        if (i > 1)
            iter.remove();

    }

}

0
List<String> lst = new ArrayList<String>();

lst.add("Ram");
lst.add("Ram");
lst.add("Shiv");
lst.add("Boss");

Map<String, Integer> mp = new HashMap<String, Integer>();

for (String string : lst) {

    if(mp.keySet().contains(string))
    {
        mp.put(string, mp.get(string)+1);

    }else
    {
        mp.put(string, 1);
    }
}

System.out.println("=mp="+mp);

出力:

=mp= {Ram=2, Boss=1, Shiv=1}

0
Map<String,Integer> hm = new HashMap<String, Integer>();
for(String i : animals) {
    Integer j = hm.get(i);
    hm.put(i,(j==null ? 1 : j+1));
}
for(Map.Entry<String, Integer> val : hm.entrySet()) {
    System.out.println(val.getKey()+" occurs : "+val.getValue()+" times");
}

0
package traversal;

import java.util.ArrayList;
import java.util.List;

public class Occurrance {
    static int count;

    public static void main(String[] args) {
        List<String> ls = new ArrayList<String>();
        ls.add("aa");
        ls.add("aa");
        ls.add("bb");
        ls.add("cc");
        ls.add("dd");
        ls.add("ee");
        ls.add("ee");
        ls.add("aa");
        ls.add("aa");

        for (int i = 0; i < ls.size(); i++) {
            if (ls.get(i) == "aa") {
                count = count + 1;
            }
        }
        System.out.println(count);
    }
}

出力:4


Stack Overflowでは、ソリューションが機能する理由、または既存のソリューションよりも優れている理由に関する説明を追加することをお勧めします。詳細については、回答方法をご覧ください。
Samuel Liew
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.