Cara menghitung jumlah kemunculan elemen dalam Daftar


173

Saya punya ArrayList, kelas Koleksi Java, sebagai berikut:

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

Seperti yang Anda lihat, animals ArrayListterdiri dari 3 batelemen dan satu owlelemen. Saya bertanya-tanya apakah ada API dalam kerangka Koleksi yang mengembalikan jumlah batkemunculan atau jika ada cara lain untuk menentukan jumlah kemunculan.

Saya menemukan bahwa Koleksi Google Multisetmemang memiliki API yang mengembalikan jumlah kemunculan elemen. Tapi itu hanya kompatibel dengan JDK 1.5. Produk kami saat ini di JDK 1.6, jadi saya tidak bisa menggunakannya.


Itulah salah satu alasan mengapa Anda harus memprogram ke antarmuka daripada implementasi. Jika Anda menemukan koleksi yang tepat, Anda harus mengubah jenis untuk menggunakan koleksi itu. Saya akan mengirim jawaban untuk ini.
OscarRyz

Jawaban:


333

Saya cukup yakin metode frekuensi statis dalam Koleksi akan berguna di sini:

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

Begitulah cara saya melakukannya. Saya cukup yakin ini jdk 1.6 lurus ke atas.


Selalu lebih suka Api dari JRE, yang menambah ketergantungan lain pada proyek. Dan jangan Menciptakan Kembali Roda !!
Fernando.

Itu diperkenalkan di JDK 5 (meskipun tidak ada yang menggunakan versi sebelumnya sehingga tidak masalah) docs.oracle.com/javase/8/docs/technotes/guides/collections/…
Minion Jim

105

Di Jawa 8:

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

6
Menggunakan Function.identity () (dengan impor statis) alih-alih e -> e membuatnya sedikit lebih baik untuk dibaca.
Kuchi

8
Mengapa ini lebih baik daripada Collections.frequency()? Sepertinya kurang mudah dibaca.
rozina

Ini bukan yang diminta. Itu bekerja lebih dari yang diperlukan.
Alex Worden

8
Ini mungkin melakukan lebih dari apa yang diminta, tetapi ia melakukan persis apa yang saya inginkan (dapatkan peta elemen yang berbeda dalam daftar sesuai perhitungan mereka). Selanjutnya, pertanyaan ini adalah hasil teratas di Google ketika saya mencari.
KJP

@rozina Anda mendapatkan semua hitungan dalam satu pass.
atoMerz

22

Ini menunjukkan, mengapa penting untuk " Merujuk ke objek dengan antarmuka mereka " seperti yang dijelaskan dalam buku Java yang Efektif .

Jika Anda membuat kode untuk implementasi dan menggunakan ArrayList di katakanlah, 50 tempat dalam kode Anda, ketika Anda menemukan implementasi "Daftar" yang baik yang menghitung item, Anda harus mengubah semua 50 tempat itu, dan mungkin Anda harus pecahkan kode Anda (jika hanya digunakan oleh Anda tidak ada masalah besar, tetapi jika digunakan oleh orang lain menggunakan, Anda juga akan memecahkan kode mereka)

Dengan memprogram ke antarmuka, Anda dapat membiarkan 50 tempat itu tidak berubah dan mengganti implementasi dari ArrayList ke "CountItemsList" (misalnya) atau kelas lain.

Di bawah ini adalah contoh yang sangat mendasar tentang bagaimana ini bisa ditulis. Ini hanya sampel, Daftar siap produksi akan jauh lebih rumit.

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" ));
    }
}

Prinsip-prinsip OO yang diterapkan di sini: pewarisan, polimorfisme, abstraksi, enkapsulasi.


12
Yah kita harus selalu mencoba komposisi daripada warisan. Implementasi Anda sekarang macet ke ArrayList ketika ada saat-saat Anda ingin LinkedList atau lainnya. Contoh Anda seharusnya mengambil LIst lain di konstruktor / pabriknya dan mengembalikan pembungkusnya.
mP.

Saya sepenuhnya setuju dengan Anda. Alasan saya menggunakan pewarisan dalam sampel adalah karena jauh lebih mudah untuk menunjukkan contoh yang sedang berjalan menggunakan pewarisan daripada komposisi (harus mengimplementasikan antarmuka Daftar). Warisan menciptakan kopling tertinggi.
OscarRyz

2
Tetapi dengan menamakannya CountItemsList Anda menyiratkan bahwa ia melakukan dua hal, itu menghitung item dan itu adalah daftar. Saya pikir hanya satu tanggung jawab tunggal untuk kelas itu, menghitung kejadian, akan sesederhana dan Anda tidak perlu mengimplementasikan antarmuka Daftar.
flob

11

Maaf tidak ada pemanggilan metode sederhana yang dapat melakukannya. Yang perlu Anda lakukan hanyalah membuat peta dan menghitung frekuensi dengannya.

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); }
}

Ini benar-benar bukan solusi yang skalabel - bayangkan kumpulan data MM memiliki ratusan dan ribuan entri dan MM ingin mengetahui frekuensi untuk setiap entri. Ini berpotensi menjadi tugas yang sangat mahal - terutama ketika ada banyak cara yang lebih baik untuk melakukannya.
mP.

Ya, itu mungkin bukan solusi yang baik, tidak berarti itu salah.
Adeel Ansari

1
@dehmann, saya tidak berpikir dia benar-benar menginginkan jumlah kejadian kelelawar dalam koleksi 4-elemen, saya pikir itu hanya sampel data sehingga kami akan mengerti lebih baik :-).
paxdiablo

2
@Vinegar 2/2. Pemrograman adalah tentang melakukan hal-hal dengan benar sekarang, jadi kami tidak menyebabkan sakit kepala atau pengalaman buruk bagi orang lain baik itu pengguna atau pembuat kode lain di masa depan. PS: Semakin banyak kode yang Anda tulis, semakin besar kemungkinan ada kesalahan.
mP.

2
@ MP: Tolong jelaskan mengapa ini bukan solusi yang dapat diskalakan. Ray Hidayat sedang membangun penghitungan frekuensi untuk setiap token sehingga setiap token kemudian dapat dilihat. Apa solusi yang lebih baik?
stackoverflowuser2010

10

Tidak ada metode asli di Jawa untuk melakukannya untuk Anda. Namun, Anda dapat menggunakan IterableUtils # countMatches () dari Apache Commons-Collections untuk melakukannya untuk Anda.


Lihat jawaban saya di bawah ini - jawaban yang benar adalah dengan menggunakan struktur yang mendukung gagasan penghitungan dari awal, bukan menghitung entri dari awal hingga akhir setiap kali kueri dibuat.
mP.

@ MP Jadi, Anda hanya downvote semua orang yang memiliki pendapat yang berbeda dari Anda? Bagaimana jika dia tidak dapat menggunakan Tas karena suatu alasan atau terjebak dengan menggunakan salah satu Koleksi asli?
Kevin

-1 untuk menjadi pecundang :-) Saya pikir mP menurunkan Anda karena solusi Anda menghabiskan waktu setiap kali Anda menginginkan hasilnya. Satu tas berharga sedikit waktu hanya saat dimasukkan. Seperti halnya database, struktur semacam ini cenderung "lebih banyak membaca daripada menulis" sehingga masuk akal untuk menggunakan opsi berbiaya rendah.
paxdiablo

Dan tampaknya jawaban Anda juga membutuhkan hal-hal yang bukan asli, sehingga komentar Anda agak aneh.
paxdiablo

Terima kasih untuk kalian berdua. Saya percaya salah satu dari dua pendekatan atau keduanya mungkin berhasil. Saya akan mencobanya besok.
MM.

9

Sebenarnya, kelas Koleksi memiliki metode statis yang disebut: frekuensi (Koleksi c, Objek o) yang mengembalikan jumlah kemunculan elemen yang Anda cari, omong-omong, ini akan bekerja dengan sempurna untuk Anda:

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 memposting jawaban yang sama 5 tahun sebelum Anda.
Fabian Barney

9

Alternatif Java 8 solusi menggunakan Streaming :

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

8

Saya ingin tahu, mengapa Anda tidak dapat menggunakan Google Collection API itu dengan JDK 1.6. Apakah dikatakan demikian? Saya pikir Anda bisa, seharusnya tidak ada masalah kompatibilitas, karena dibuat untuk versi yang lebih rendah. Kasing akan berbeda jika yang dibangun untuk 1,6 dan Anda menjalankan 1,5.

Apakah saya salah di suatu tempat?


Mereka telah dengan jelas menyebutkan bahwa mereka sedang dalam proses meningkatkan api mereka ke jdk 1.6.
MM.

1
Itu tidak membuat yang lama tidak kompatibel. Melakukannya?
Adeel Ansari

Seharusnya tidak. Tapi cara mereka membuang disclaimer, membuat saya tidak nyaman menggunakannya dalam versi 0.9
MM mereka.

Kami menggunakannya dengan 1.6. Di mana katanya hanya kompatibel dengan 1,5?
Patrick

2
Dengan "meningkatkan ke 1,6" mereka mungkin berarti "meningkatkan untuk mengambil keuntungan dari hal-hal baru di 1,6," tidak "memperbaiki kompatibilitas dengan 1,6".
Adam Jaskiewicz

6

Pendekatan yang sedikit lebih efisien mungkin

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

Untuk mendapatkan kemunculan objek dari daftar secara langsung:

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

Untuk mendapatkan kemunculan koleksi Objek di dalam daftar, ganti metode equals di kelas Object sebagai:

@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;
}

Panggil Collections.frequency sebagai:

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

6

Cara sederhana untuk menemukan terjadinya nilai string dalam array menggunakan fitur 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);
    }

Output: {Cat = 2, Kambing = 1, Sapi = 1, sapi = 1, Anjing = 1}

Anda dapat melihat "Sapi" dan sapi tidak dianggap sebagai string yang sama, jika Anda memerlukannya dalam jumlah yang sama, gunakan .toLowerCase (). Temukan cuplikan di bawah untuk hal yang sama.

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

Output: {cat = 2, sapi = 2, kambing = 1, anjing = 1}


nit: karena daftar adalah daftar string, toString()tidak perlu. Anda bisa melakukan:duplicateList.stream().collect(Collectors.groupingBy(e -> e,Collectors.counting()));
Tad

5

Yang Anda inginkan adalah Tas - yang seperti satu set tetapi juga menghitung jumlah kejadian. Sayangnya kerangka java Collections - hebat karena mereka tidak memiliki Bag impl. Untuk itu orang harus menggunakan teks tautan Apache Common Collection


1
Solusi scalable terbaik dan, jika Anda tidak bisa menggunakan barang pihak ketiga, cukup tulis sendiri. Tas bukan ilmu roket untuk membuat. +1.
paxdiablo

Diturunkan karena memberikan beberapa jawaban yang tidak jelas sementara yang lain telah menyediakan implementasi untuk struktur data penghitungan frekuensi. Struktur data 'kantong' yang Anda tautkan juga bukan solusi yang tepat untuk pertanyaan OP; bahwa struktur 'kantung' dimaksudkan untuk menampung sejumlah salinan token tertentu, bukan untuk menghitung jumlah kemunculan token.
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");

Metode 1:

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

for (String s : set) {

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

Metode 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);

Selamat Datang di Stack Overflow! Pertimbangkan untuk menjelaskan kode Anda untuk mempermudah orang lain memahami solusi Anda.
Antimony

2

Jika Anda menggunakan Eclipse Collections , Anda dapat menggunakan a Bag. A MutableBagdapat dikembalikan dari implementasi apa pun RichIterabledengan menelepon 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"));

The HashBagimplementasi dalam Eclipse Koleksi didukung oleh MutableObjectIntMap.

Catatan: Saya pengendara untuk Eclipse Collections.


1

Masukkan elemen-elemen dari daftar array di hashMap untuk menghitung frekuensi.


Ini persis sama dengan yang dikatakan tweakt dengan contoh kode.
mP.

1

Java 8 - metode lain

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

0

Jadi lakukan dengan cara lama dan gulir sendiri:

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++);
}

Dengan "disinkronkan" yang tepat, jika perlu, untuk menghindari kondisi balapan. Tapi saya masih lebih suka melihat ini di kelasnya sendiri.
paxdiablo

Anda salah ketik. Perlu HashMap sebagai gantinya, karena Anda membawanya di Peta. Tetapi kesalahan untuk menempatkan 0 bukannya 1 sedikit lebih serius.
Adeel Ansari

0

Jika Anda adalah pengguna DSL ForEach saya , itu bisa dilakukan dengan Countkueri.

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

0

Saya tidak ingin membuat kasus ini lebih sulit dan membuatnya dengan dua iterator Saya memiliki HashMap dengan LastName -> FirstName. Dan metode saya harus menghapus item dengan menculik 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);

Keluaran:

=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);
    }
}

Output: 4


Ini praktik yang baik di Stack Overflow untuk menambahkan penjelasan mengapa solusi Anda harus bekerja atau lebih baik daripada solusi yang ada. Untuk informasi lebih lanjut baca Cara Menjawab .
Samuel Liew
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.