Listedeki bir öğenin yineleme sayısı nasıl hesaplanır


173

ArrayListAşağıdaki gibi bir Java Koleksiyonu sınıfına sahibim :

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

Gördüğünüz gibi animals ArrayList3 bateleman ve bir owlelemandan oluşmaktadır. Koleksiyon çerçevesinde herhangi bir API batolup olmadığını veya olay sayısını belirleyen başka bir yol olup olmadığını merak ediyordum .

Google'ın Koleksiyonunda Multisetbir öğenin toplam tekrarlama sayısını döndüren bir API olduğunu buldum . Ancak bu yalnızca JDK 1.5 ile uyumludur. Ürünümüz şu anda JDK 1.6'da, bu yüzden kullanamıyorum.


Bir uygulama yerine bir arabirime programlamanızın nedenlerinden biri de budur. Doğru koleksiyonu bulursanız, bu koleksiyonu kullanmak için türü değiştirmeniz gerekir. Bu konuda bir cevap göndereceğim.
OscarRyz

Yanıtlar:


334

Koleksiyonlardaki statik frekans yönteminin burada kullanışlı olacağından eminim:

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

Zaten böyle yaparım. Bunun jdk 1.6 dümdüz olduğuna eminim.


Her zaman projeye başka bir bağımlılık katan JRE'den Api'yi tercih edin. Ve tekerleği yeniden icat etmeyin !!
Fernando.

JDK 5'te tanıtıldı (hiç kimse bundan önce bir sürüm kullanmasa da, önemi yok) docs.oracle.com/javase/8/docs/technotes/guides/collections/…
Minion Jim

105

Java 8'de:

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

6
E -> e yerine Function.identity () (statik içe aktarma ile) kullanılması okumayı biraz daha hoş hale getirir.
Kuchi

8
Bu neden daha iyi Collections.frequency()? Daha az okunabilir görünüyor.
rozina

İstenen bu değil. Gerekenden daha fazla iş yapar.
Alex Worden

8
Bu, istenenden daha fazlasını yapabilir, ancak tam olarak istediğimi yapar (bir listedeki farklı öğelerin sayılarına bir haritasını alın). Ayrıca, bu soru arama yaptığımda Google'daki en iyi sonuçtu.
KJP

@rozina Tüm sayıları bir geçişte alırsınız.
atoMerz

22

Bu, Etkili Java'da açıklandığı gibi " Nesnelerin arayüzlerine göre başvurulmasının " neden önemli olduğunu gösterir. kitabında .

Uygulamayı kodlar ve ArrayList'i kodunuzda 50 yer kullanırsanız, öğeleri sayan iyi bir "Liste" uygulaması bulduğunuzda, bu 50 yeri de değiştirmeniz gerekir ve muhtemelen kodunuzu kırın (yalnızca sizin tarafınızdan kullanılıyorsa, büyük bir anlaşma yoktur, ancak başka biri tarafından kullanılıyorsa, kodlarını da kırarsınız)

Arabirime programlayarak, bu 50 yeri değiştirmeden bırakabilir ve uygulamayı ArrayList'ten "CountItemsList" (örneğin) veya başka bir sınıfa değiştirebilirsiniz.

Aşağıda bunun nasıl yazılabileceğine dair çok temel bir örnek verilmiştir. Bu sadece bir örnek, üretime hazır bir liste çok daha karmaşık olurdu .

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

Burada uygulanan OO ilkeleri: kalıtım, polimorfizm, soyutlama, kapsülleme.


12
Kişi kalıtımdan ziyade daima kompozisyonu denemelidir. LinkedList veya başka bir zaman istediğiniz zaman uygulamanız artık ArrayList'e yapışmıştır. Örneğinizin yapıcısında / fabrikasında başka bir LIst almış ve bir paketleyici iade etmiş olmalıdır.
mP.

Sana tamamen katılıyorum. Örnekte kalıtım kullanmamın nedeni, kalıtım kullanarak çalışan bir örneği göstermekten (Liste arabirimini uygulamak zorunda kalmaktan) çok daha kolay olmasıdır. Kalıtım en yüksek bağlantıyı oluşturur.
OscarRyz

2
Ancak CountItemsList adını vererek iki şey yaptığını ima edersiniz, öğeleri sayar ve bir listedir. Bence bu sınıf için tek bir sorumluluk, olayları saymak o kadar basit olurdu ve Liste arayüzünü uygulamanız gerekmeyecek.
flob

11

Üzgünüm, bunu yapabileceğiniz basit bir yöntem çağrısı yok. Yine de yapmanız gereken tek şey bir harita oluşturmak ve onunla frekans saymaktır.

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

Bu gerçekten ölçeklenebilir bir çözüm değil - MM'nin veri kümesinde yüzlerce ve binlerce giriş olduğunu ve MM'nin her giriş için frekansları bilmek istediğini düşünün. Bu potansiyel olarak çok maliyetli bir iş olabilir - özellikle de bunu yapmanın çok daha iyi yolları olduğunda.
mP.

Evet, iyi bir çözüm olmayabilir, yanlış olduğu anlamına gelmez.
Adeel Ansari

1
@dehmann, tam anlamıyla 4 elementli bir koleksiyondaki yarasa oluşumlarını istediğini sanmıyorum, bence bu sadece örnek verilerdi, bu yüzden daha iyi anlayacağız :-).
paxdiablo

2
@ Üzüm 2/2. Programlama şimdi işleri düzgün yapmakla ilgilidir, bu nedenle gelecekte bir kullanıcı veya başka bir kodlayıcı olsun baş ağrısına veya kötü bir deneyime neden olmayız. Not: Yazdığınız kod ne kadar çok olursa bir şeylerin yanlış gitme şansı o kadar artar.
mP.

2
mP: Lütfen bunun neden ölçeklenebilir bir çözüm olmadığını açıklayın. Ray Hidayat, her token için bir frekans sayısı oluşturuyor, böylece her token daha sonra aranabiliyor. Daha iyi bir çözüm nedir?
stackoverflowuser2010

10

Java'da bunu sizin için yapmanın yerel bir yöntemi yoktur. Ancak, Apache Commons Koleksiyonlarından IterableUtils # countMatches () yöntemini kullanarak bunu sizin için yapabilirsiniz.


Aşağıdaki cevabıma bakın - doğru cevap, her sorgu yapıldığında girişleri baştan sona saymak yerine, başlangıçtan itibaren sayma fikrini destekleyen bir yapı kullanmaktır.
mP.

@ Yani sizden farklı bir fikri olan herkesi küçümsüyor musunuz? Bir Torbayı herhangi bir nedenle kullanamıyorsa veya yerel Koleksiyonlardan birini kullanmakta sıkışırsa ne olur?
Kevin

Ağrı kaybeden olmak için -1 :-) Sanırım mP sizi düşürdü çünkü çözümünüz her sonuç istediğinizde zaman alıyor. Bir torbanın sadece yerleştirme sırasında biraz zamanı vardır. Veritabanları gibi, bu tür yapılar da "yazmadan daha fazla okunur" olma eğilimindedir, bu nedenle düşük maliyetli seçeneği kullanmak mantıklıdır.
paxdiablo

Görünüşe göre cevabınız yerli olmayan şeyler de gerektiriyor, bu nedenle yorumunuz biraz garip görünüyor.
paxdiablo

İkinize de teşekkürler. İki yaklaşımdan birinin veya her ikisinin de işe yarayabileceğine inanıyorum. Yarın deneyeceğim.
MM.

9

: Aslında Koleksiyonları sınıf denilen bir statik yöntem vardır frekans (Koleksiyon c Nesne o), bu sizin için mükemmel çalışacak arada, aradığınız elemanın yineleme sayısını döndürür:

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 de aynı cevabı sizinkinden 5 yıl önce yayınladı.
Fabian Barney

9

Akışları kullanan alternatif Java 8 çözümü :

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

8

Acaba, neden Google'ın Koleksiyon API'sını JDK 1.6 ile kullanamıyorsunuz? Öyle mi diyor? Bence, daha düşük bir sürüm için oluşturulduğu için herhangi bir uyumluluk sorunu olmamalıdır. 1.6 için yapılmış olsaydı ve 1.5 çalıştırıyorsanız durum farklı olurdu.

Bir yerde yanlış mıyım?


Api'lerini jdk 1.6'ya yükseltme sürecinde olduklarını açıkça belirtmişlerdir.
MM.

1
Bu eskiyi uyumsuz yapmaz. Yapar?
Adeel Ansari

Olmamalı. Ancak feragatnameleri atma şekli, 0.9 sürümlerinde
MM)

1.6 ile kullanıyoruz. Nerede sadece 1.5 ile uyumlu olduğunu söylüyor?
Patrick

2
"1.6'ya yükseltmek" ile, "1.6 ile uyumluluğun düzeltilmesi değil" 1.6'daki yeni özelliklerden yararlanmak için yükseltmek anlamına gelir.
Adam Jaskiewicz

6

Biraz daha verimli bir yaklaşım

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

Nesnenin oluşumlarını doğrudan listeden almak için:

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

İçeride Nesne koleksiyonunun oluşmasını sağlamak için, Object sınıfındaki equals yöntemini şu şekilde geçersiz kılın:

@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 öğesini şu şekilde arayın:

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

6

Java 8 özelliklerini kullanarak bir dizede dize değerinin oluşumunu bulmanın basit yolu.

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

Çıktı: {Kedi = 2, Keçi = 1, İnek = 1, inek = 1, Köpek = 1}

"İnek" ve inek aynı dize olarak değerlendirilmez, aynı sayı altında gerekli olması durumunda .toLowerCase () kullanın. Bunun için lütfen aşağıdaki pasajı bulun.

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

Çıktı: {cat = 2, inek = 2, keçi = 1, köpek = 1}


nit: liste dizelerin bir listesi toString()olduğu için gereksizdir. duplicateList.stream().collect(Collectors.groupingBy(e -> e,Collectors.counting()));
Tad

5

İstediğiniz bir Çanta - bir set gibi ama aynı zamanda meydana gelen olayların sayısını da sayar. Ne yazık ki java Koleksiyonlar çerçevesi - harika bir Çanta impl yok gibi. Bunun için Apache Common Collection bağlantı metnini kullanmalısınız


1
En iyi ölçeklenebilir çözüm ve üçüncü taraf şeyleri kullanamıyorsanız, sadece kendiniz yazın. Çantalar oluşturmak roket bilimi değildir. +1.
paxdiablo

Diğerleri frekans sayımı veri yapıları için uygulamalar sağlarken bazı belirsiz cevaplar vermek için aşağı oy verildi. Bağlandığınız 'çanta' veri yapısı da OP'nin sorusuna uygun bir çözüm değildir; bu 'torba' yapısının, belirteçlerin tekrarlama sayısını saymamak için bir tokenin belirli sayıda kopyasını tutması amaçlanmıştır.
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");

Yöntem 1:

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

for (String s : set) {

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

Yöntem 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'a hoş geldiniz! Başkalarının çözümünüzü anlamasını kolaylaştırmak için kodunuzu açıklamayı düşünün.
Antimon

2

Eğer kullanırsanız Eclipse Koleksiyonlar , bir kullanabilirsiniz Bag. A MutableBagherhangi uygulanmasından iade edilebilir RichIterablearayarak 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 koleksiyonlarında uygulama a tarafından desteklenmektedirMutableObjectIntMap .

Not: Eclipse Collections için bir komisyoncuyum.


1

Frekansı saymak için arraylist öğelerini hashMap'e koyun.


Bu, tweakt'ın bir kod örneğiyle söylediği şeyle tamamen aynıdır.
mP.

1

Java 8 - başka bir yöntem

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

0

Öyleyse eski moda bir şekilde yapın ve kendiniz yapın:

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

Yarış koşullarından kaçınmak için gerekirse uygun "senkronize" ile. Ama yine de bunu kendi sınıfında görmeyi tercih ederim.
paxdiablo

Bir yazım hatası var. Haritaya eklediğiniz için bunun yerine HashMap'e ihtiyacınız var. Ancak 1 yerine 0 koyma hatası biraz daha ciddidir.
Adeel Ansari

0

ForEach DSL'imin bir kullanıcısıysanız , bunu bir Countsorgu ile yapabilirsiniz .

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

0

Bu davayı daha zor hale getirmek istemedim ve iki yineleyici ile yaptım LastName -> FirstName ile bir HashMap var. Ve yöntemim dulicate FirstName içeren öğeleri silmeli.

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

Çıktı:

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

Çıktı: 4


Çözümünüzün neden çalışması ya da mevcut çözümlerden daha iyi olması gerektiğine ilişkin bir açıklama eklemek Stack Stackflow'da iyi bir uygulamadır. Daha fazla bilgi için Nasıl Yanıtlanır bölümünü okuyun .
Samuel Liew
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.