List에서 요소의 발생 횟수를 계산하는 방법


173

나는이 ArrayList다음과 같이 자바의 컬렉션 클래스를 :

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

보다시피, animals ArrayList3 개의 bat요소와 하나의 owl요소로 구성됩니다. Collection 프레임 워크에 bat발생 횟수를 반환하는 API가 있는지 또는 발생 횟수를 결정하는 다른 방법 이 있는지 궁금합니다 .

Google의 Collection Multiset에는 요소의 총 발생 횟수를 반환하는 API가 있습니다. 그러나 이는 JDK 1.5 와만 호환됩니다. 당사 제품은 현재 JDK 1.6에 있으므로 사용할 수 없습니다.


이것이 구현이 아닌 인터페이스에 프로그래밍해야하는 이유 중 하나입니다. 올바른 모음을 찾으면 해당 모음을 사용하도록 유형을 변경해야합니다. 이에 대한 답변을 게시하겠습니다.
OscarRyz

답변:


333

Collections의 정적 주파수 방법이 여기에 유용 할 것이라고 확신합니다.

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

어쨌든 내가 그렇게하는 방법입니다. 나는 이것이 jdk 1.6이라고 확신합니다.


프로젝트에 다른 의존성을 추가하는 JRE의 Api를 항상 선호합니다. 그리고 바퀴를 재발 명하지 마십시오!
페르난도.


105

자바 8 :

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

6
e-> e 대신 Function.identity () (정적 가져 오기 사용)를 사용하면 조금 더 읽기 좋습니다.
Kuchi

8
왜 이것보다 낫 Collections.frequency()습니까? 읽기 어려운 것 같습니다.
rozina April

이것은 요구 된 것이 아닙니다. 필요 이상으로 많은 일을합니다.
Alex Worden

8
이것은 요청 된 것보다 더 많은 것을 할 수 있지만, 내가 원하는 것을 정확하게 수행합니다 (목록에있는 고유 한 요소를 카운트로 가져옵니다). 또한이 질문은 내가 검색했을 때 Google의 최고 결과였습니다.
KJP

@rozina 당신은 한 번에 모든 카운트를 얻을 수 있습니다.
atoMerz

22

이는 효과적인 Java 서적에 설명 된대로 " 인터페이스로 오브젝트 참조 "가 중요한 이유를 보여줍니다 .

구현에 코드를 작성하고 ArrayList를 사용하여 코드의 50 개 장소를 가정 해 봅시다. 항목을 세는 훌륭한 "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" ));
    }
}

여기에 적용되는 OO 원칙 : 상속, 다형성, 추상화, 캡슐화.


12
잘 상속받지 말고 항상 구성을 시도해야합니다. LinkedList 또는 다른 시간을 원할 때 구현이 ArrayList에 붙어 있습니다. 예제는 생성자 / 공장에서 다른 LIst를 가져 와서 래퍼를 반환해야합니다.
mP.

나는 당신에게 완전히 동의합니다. 샘플에서 상속을 사용한 이유는 컴포지션 (List 인터페이스를 구현해야 함)보다 상속을 사용하여 실행중인 예제를 표시하는 것이 훨씬 더 쉽기 때문입니다. 상속은 가장 높은 커플 링을 만듭니다.
OscarRyz

2
그러나 CountItemsList라는 이름을 지정하면 두 가지 작업을 수행하며 항목을 계산하며 목록입니다. 그 클래스에 대한 하나의 책임, 발생 횟수를 계산하는 것만 큼 간단하고 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

2
@ 식초 2/2. 프로그래밍은 현재 일을 올바르게 수행하는 것이므로 앞으로 사용자 또는 다른 코더가 될 수 있습니다. 추신 : 코드를 많이 작성할수록 문제가 발생할 가능성이 높아집니다.
mP.

2
@mP : 이것이 확장 가능한 솔루션이 아닌 이유를 설명하십시오. Ray Hidayat은 각 토큰을 조회 할 수 있도록 각 토큰에 대한 빈도 수를 구축하고 있습니다. 더 나은 해결책은 무엇입니까?
stackoverflowuser2010 년

10

Java에는이를위한 기본 메소드가 없습니다. 그러나 Apache Commons-Collections의 IterableUtils # countMatches () 를 사용 하여 대신 할 수 있습니다.


아래의 답변을 참조하십시오. 정답은 쿼리를 만들 때마다 처음부터 끝까지 항목을 계산하는 것이 아니라 처음부터 계산 아이디어를 지원하는 구조를 사용하는 것입니다.
mP.

@mP 그래서 당신은 당신과 다른 의견을 가진 모든 사람을 공감합니다. 어떤 이유로 가방을 사용할 수 없거나 기본 컬렉션 중 하나를 사용하지 않으면 어떻게 되나요?
Kevin

-1 아픈 패배자 :-) 당신이 결과를 원할 때마다 솔루션 비용이 들기 때문에 mP가 당신을 downvoted 생각합니다. 가방은 삽입시 약간의 시간이 소요됩니다. 데이터베이스와 마찬가지로 이러한 종류의 구조는 "쓰기보다 더 읽기"경향이 있으므로 저렴한 옵션을 사용하는 것이 좋습니다.
paxdiablo 2013

그리고 귀하의 답변에도 기본이 아닌 내용이 필요하므로 귀하의 의견은 약간 이상해 보입니다.
paxdiablo 2013

둘 다 고마워 두 가지 방법 중 하나 또는 둘 다 작동 할 수 있다고 생각합니다. 내일 시험해 볼게요.
MM.

9

실제로 Collections 클래스에는 다음과 같은 정적 메소드가 있습니다 : frequency (Collection c, Object 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

스트림 을 사용하는 대체 Java 8 솔루션 :

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

8

JDK 1.6에서 Google의 Collection API를 사용할 수없는 이유가 궁금합니다. 그렇게 말합니까? 나는 당신이 할 수 있다고 생각합니다. 낮은 버전을 위해 만들어 졌기 때문에 호환성 문제가 없어야합니다. 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에서 새로운 것을 활용하기 위해 업그레이드"를 의미합니다.
Adam 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 collection inside list를 얻으려면 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()));
Tad

5

당신이 원하는 것은 가방과 같습니다-이것은 세트와 같지만 발생 횟수를 계산합니다. 불행히도 Java Collections 프레임 워크-Bag impl이 없기 때문에 훌륭합니다. 이를 위해서는 Apache Common Collection 링크 텍스트를 사용해야합니다


1
확장 성이 가장 좋은 솔루션이며 타사 제품을 사용할 수없는 경우 직접 작성하십시오. 가방은 로켓 과학이 아닙니다. +1.
paxdiablo 2013

일부는 모호한 답변을 제공 한 반면 다른 이들은 주파수 계산 데이터 구조에 대한 구현을 제공했습니다. 연결 한 '가방'데이터 구조도 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);

스택 오버플로에 오신 것을 환영합니다! 다른 사람들이 귀하의 솔루션을 쉽게 이해할 수 있도록 코드를 설명하십시오.
안티몬

2

Eclipse Collections 를 사용하는 경우을 사용할 수 있습니다 Bag. 의 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 Collections 의 구현은로 지원됩니다 MutableObjectIntMap.

참고 : 저는 Eclipse Collections의 커미터입니다.


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 2013

오타가 있습니다. Map에서 가져갈 때 대신 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이있는 두 개의 반복자로 만들었습니다. 그리고 내 방법은 중복 된 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.