2015-01-31 5 views

risposta

11

Dalla documentazione che si è collegato:

The Counter la classe è simile a borse o multiset in altre lingue.

Java non ha una classe Multiset o un analogo. Guava ha una collezione MultiSet, che fa esattamente quello che vuoi.

in puro Java, è possibile utilizzare un Map<T, Integer> e il nuovo merge metodo:

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

counts.merge("Test", 1, Integer::sum); 
counts.merge("Test", 1, Integer::sum); 
counts.merge("Other", 1, Integer::sum); 
counts.merge("Other", 1, Integer::sum); 
counts.merge("Other", 1, Integer::sum); 

System.out.println(counts.getOrDefault("Test", 0)); 
System.out.println(counts.getOrDefault("Other", 0)); 
System.out.println(counts.getOrDefault("Another", 0)); 

uscita:

2 
3 
0 

Puoi avvolgere questo comportamento in un class in poche righe di codice:

public class Counter<T> { 
    final Map<T, Integer> counts = new HashMap<>(); 

    public void add(T t) { 
     counts.merge(t, 1, Integer::sum); 
    } 

    public int count(T t) { 
     return counts.getOrDefault(t, 0); 
    } 
} 

Uso come questo:

final Counter<String> counts = new Counter<>(); 

counts.add("Test"); 
counts.add("Test"); 
counts.add("Other"); 
counts.add("Other"); 
counts.add("Other"); 

System.out.println(counts.count("Test")); 
System.out.println(counts.count("Other")); 
System.out.println(counts.count("Another")); 

uscita:

2 
3 
0 
+0

Risposta brillante! –

+0

Unire ha appena reso la mia giornata. Eccellente. – dantiston

7

Non per quanto ne so. Ma Scala è molto espressivo, che consente di cucinare qualcosa di simile a voi stessi:

def counts[T](s: Seq[T]) = s.groupBy(x => x).mapValues(_.length) 

Edit: Ancora più conciso con:

def counts[T](s: Seq[T]) = s.groupBy(identity).mapValues(_.length) 
+0

Ma l'OP voleva qualcosa di diverso da "raccogliere tutti gli oggetti e quindi raggrupparli"? –

+0

@Paul Voleva anche qualcosa come 'collections.Counter'. Ora date un'occhiata al sorgente Python, sembra che Python faccia la stessa cosa del frammento di scala che ho scritto. http://svn.python.org/projects/python/trunk/Lib/collections.py – Dave

2

Per lo più si dovrebbe essere buono con le operazioni di base concatenati. Come:

val s = Seq("apple", "oranges", "apple", "banana", "apple", "oranges", "oranges") 
s.groupBy(l => l).map(t => (t._1, t._2.length)) //1 
s.count(_ == "apple") //2 

Con quale risultato:

Map(banana -> 1, oranges -> 3, apple -> 3) //1 - result 
3 //2 - result 
3

Un'altra versione Scala, farlo in un solo passaggio ed evitando .groupBy

val l = List("a", "b", "b", "c", "b", "c", "b", "d") 

l.foldLeft(Map[String, Int]() withDefaultValue (0)) 
      { (m, el) => m updated (el, m(el)+1)} 
//> res1: Map(a -> 1, b -> 4, c -> 2, d -> 1) 

o se non si vuole una mappa con valore di default pari a zero

l.foldLeft(Map[String, Int]()) { (m, el) => m updated (el, m.getOrElse(el,0)+1)} 
0

Molti anni s dopo che ho fatto questa domanda originariamente mi sono reso conto di quanto fosse banale.La mia soluzione ultra-base Scala è:

import scala.collection.mutable 

/** 
    * Created by salim on 3/10/2017. 
    */ 
case class Counter[T]() { 
    lazy val state:mutable.Map[T,Int] = mutable.HashMap[T,Int]() 
    def apply(i:T):Int = state.getOrElse(i,0) 
    def count(i:T):Unit = { 
    val newCount = 1 + this(i) 
    state += (i -> newCount) 
    } 
} 
0

Qui è la mia coda implementazione ricorsiva Scala utilizzando una mappa mutevole

def counter[T](s: Seq[T]) = { 
    import scala.collection.mutable.Map 
    def counter_iter[T](s: Seq[T], m: Map[T, Int]): Map[T, Int]= { 
    if (s.isEmpty) m 
    else { 
     m(s.head) += 1 
     counter_iter(s.tail, m) 
    } 
    } 
    counter_iter(s, Map[T, Int]().withDefaultValue(0)) 
} 

da usare:

scala> counter(List(1,1,2,2,2,3,4)) 
res34: scala.collection.mutable.Map[Int,Int] = Map(2 -> 3, 4 -> 1, 1 -> 2, 3 -> 1) 
Problemi correlati