Java List. contains (필드 값이 x 인 객체)


199

List특정 값을 가진 필드가있는 객체가 포함되어 있는지 확인하고 싶습니다 . 이제 루프를 사용하여 확인하고 더 효율적인 코드가 있는지 궁금했습니다.

같은 것;

if(list.contains(new Object().setName("John"))){
    //Do some stuff
}

위의 코드는 아무것도하지 않는다는 것을 알고 있습니다. 단지 달성하려는 것을 대략 보여줍니다.

또한 명확히하기 위해 간단한 루프를 사용하지 않으려는 이유는이 코드가 현재 루프 내부의 루프 내부에있는 루프 내부에 있기 때문입니다. 가독성을 위해 루프에 루프를 계속 추가하고 싶지 않습니다. 그래서 간단한 대안이 있는지 궁금했습니다.


5
이것이 사용자 정의 평등이므로 사용자 정의 코드를 작성해야합니다.
Sotirios Delimanolis

명시된 목표와 코드 예제가 일치하지 않는 것 같습니다. 하나의 필드 값을 기준으로 만 객체를 비교 하시겠습니까?
Duncan Jones

1
equals(Object)사용자 정의 객체 의 메소드를 재정의 하시겠습니까?
Josh M

1
for(Person p:list) if (p.getName().equals("John") return true; return false;Java에서 더 간결한 방법을 찾지 못할 것입니다.
MikeFHay

@Rajdeep 죄송합니다. 귀하의 질문을 이해하지 못합니다. p.equals(p) 해야 당신이 달성하려고하는지 혼동하고있어, 그래서 항상 사실. 당신이 경우 희망 새 질문을 물어 당신은 더 나은 도움을받을 수 있습니다.
MikeFHay

답변:


267

스트림

Java 8을 사용하는 경우 다음과 같이 시도해 볼 수 있습니다.

public boolean containsName(final List<MyObject> list, final String name){
    return list.stream().filter(o -> o.getName().equals(name)).findFirst().isPresent();
}

또는 다음과 같이 시도해 볼 수 있습니다.

public boolean containsName(final List<MyObject> list, final String name){
    return list.stream().map(MyObject::getName).filter(name::equals).findFirst().isPresent();
}

이 메소드는 에 이름 trueList<MyObject>포함되어 있으면를 반환 합니다 . s 각각에 대해 작업을 수행하려면 다음과 같이 시도하십시오.MyObjectnameMyObjectgetName().equals(name)

public void perform(final List<MyObject> list, final String name){
    list.stream().filter(o -> o.getName().equals(name)).forEach(
            o -> {
                //...
            }
    );
}

여기서 인스턴스를 o나타냅니다 MyObject.

또는 의견에서 알 수 있듯이 (감사 MK10) 다음 Stream#anyMatch방법을 사용할 수 있습니다 .

public boolean containsName(final List<MyObject> list, final String name){
    return list.stream().anyMatch(o -> o.getName().equals(name));
}

1
그런 다음 두 번째 예는이어야합니다 public boolean. 또한 Objects.equals()경우에 o.getName()사용할 수도 있습니다 null.
Eric Jablow

1
나는 단지 편집증 프로그래머입니다. 나는 사람들이 빠르고 널이없는 프로젝트를 다루었으므로 방어적인 경향이있다. 항목이 null이 아닌 것을 보장하는 것이 훨씬 좋습니다.
Eric Jablow

5
@EricJablow 아마도 하나의 (광산)이 아닌 점검을 수행하지 않는 모든 답변 에 대해 언급해야 할 null것입니다.
Josh M

55
더 짧고 이해하기 쉽다 : return list.stream().anyMatch(o -> o.getName().equals(name));
MK10

3
@ MK10에 동의하지만 java.util.Objectsnullsafe 비교에 사용 return list.stream().anyMatch(o -> Objects.euqals(o.getName(), name);합니다. 스트림의 개체를 null로 확인하려면 .filter(Objects::nonNull)anyMatch 앞에 a 를 추가 할 수 있습니다 .
tobijdc

81

두 가지 선택이 있습니다.

1. 가장 바람직한 방법은 Object 클래스에서`equals ()`메서드를 재정의하는 것입니다.

예를 들어이 Object 클래스가 있다고 가정 해 보겠습니다.

public class MyObject {
    private String name;
    private String location;
    //getters and setters
}

이제 MyObject의 이름에만 관심이 있다고하자. 고유 한 이름이어야하므로 두 개의 MyObject 이름이 같은 경우 동일하다고 간주해야합니다. 이 경우 이름을 비교하여 동등성을 결정하기 위해`equals ()`메소드 (및`hashcode ()`메소드)를 대체하려고합니다.

이 작업을 완료하면 Collection에 이름이 "foo"인 MyObject가 포함되어 있는지 확인할 수 있습니다.

MyObject object = new MyObject();
object.setName("foo");
collection.contains(object);

그러나 다음과 같은 경우에는이 옵션이 적합하지 않을 수 있습니다.

  • 이름과 위치를 모두 사용하여 동등성을 확인하고 있지만 Collection에 특정 위치의 MyObject가 있는지 확인하려고합니다. 이 경우 이미 'equals ()'를 재정의했습니다.
  • `MyObject`는 자유롭게 변경할 수없는 API의 일부입니다.

이 중 하나에 해당하면 옵션 2를 원할 것입니다.

2. 자신 만의 유틸리티 메소드를 작성하십시오.

public static boolean containsLocation(Collection<MyObject> c, String location) {
    for(MyObject o : c) {
        if(o != null && o.getLocation.equals(location)) {
            return true;
        }
    }
    return false;
}

또는 ArrayList (또는 다른 컬렉션)를 확장 한 다음 고유 한 메서드를 추가 할 수 있습니다.

public boolean containsLocation(String location) {
    for(MyObject o : this) {
        if(o != null && o.getLocation.equals(location)) {
                return true;
            }
        }
        return false;
    }

불행히도 그 주위에 더 좋은 방법은 없습니다.


if (o! = null && o.getLocation.equals (location))에서 getter에 대한 괄호를 잊어 버렸다고 생각합니다.
olszi

25

구글 구아바

Guava를 사용 하는 경우 기능적인 접근 방식을 취하고 다음을 수행 할 수 있습니다.

FluentIterable.from(list).find(new Predicate<MyObject>() {
   public boolean apply(MyObject input) {
      return "John".equals(input.getName());
   }
}).Any();

조금 장황하게 보입니다. 그러나 술어는 오브젝트이며 다른 검색에 대해 다른 변형을 제공 할 수 있습니다. 라이브러리 자체가 컬렉션의 반복과 적용하려는 기능을 분리하는 방법에 유의하십시오. equals()특정 행동 을 무시할 필요가 없습니다 .

아래에 언급 된 바와 같이, Java 8 이상에 내장 된 java.util.Stream 프레임 워크는 유사한 것을 제공합니다.


1
또는 Iterables.any(list, predicate)실제로 동일한을 사용할 수 있으며 스타일 적으로 선호하거나 선호하지 않을 수 있습니다.
MikeFHay

@EricJablow Yup. :) 내 솔루션을 볼 수 있습니다.
Josh M

25

다음은 Java 8 이상을 사용하는 방법입니다.

boolean isJohnAlive = list.stream().anyMatch(o -> o.getName().equals("John"));

20

Collection.contains()equals()하나가 반환 될 때까지 각 개체 를 호출 하여 구현됩니다 true.

따라서 이것을 구현하는 한 가지 방법은 재정의하는 equals()것이지만 물론 하나만 가질 수 있습니다.

따라서 구아바 와 같은 프레임 워크 는이를 위해 술어를 사용합니다. 을 사용 Iterables.find(list, predicate)하면 테스트를 술어에 넣어 임의의 필드를 검색 할 수 있습니다.

VM 위에 빌드 된 다른 언어에는이 언어가 내장되어 있습니다. 예를 들어 Groovy 에서는 다음과 같이 작성하면됩니다.

def result = list.find{ it.name == 'John' }

Java 8은 우리의 삶을 더 편하게 만들었습니다.

List<Foo> result = list.stream()
    .filter(it -> "John".equals(it.getName())
    .collect(Collectors.toList());

이런 것들에 관심이 있다면 "Beyond Java"책을 제안합니다. 여기에는 수많은 Java 단점과 다른 언어의 성능에 대한 많은 예가 포함되어 있습니다.


따라서 목록에 추가되는 사용자 정의 객체의 equals 메서드를 재정의하면 특정 클래스 변수가 동일한 경우 true를 반환하도록 할 수 있습니까?
Rudi Kershaw

1
아니요, 아이디어 equals()는 매우 제한적입니다. 그것은 "개체의 정체성"을 확인하는 것을 의미합니다. 어떤 종류의 객체에 의미가있을 수 있습니다. 필드를 포함해야하는 플래그를 추가하면 모든 종류의 문제가 발생할 수 있습니다. 나는 그것을 강력히 권합니다.
Aaron Digulla

그루비를 좋아하기 때문에 "새로운"Java 8 Lambda는 실제로이 차가움을 제공하지 않습니까? def result = list.find{ it.name == 'John' }프로그래머에 대한 전쟁 범죄로 인해 Oracle / JCP가 고소되어야합니다.
ken

19

이진 검색

Collections.binarySearch 를 사용 하여 목록에서 요소를 검색 할 수 있습니다 (목록이 정렬되어 있다고 가정).

Collections.binarySearch(list, new YourObject("a1", "b",
                "c"), new Comparator<YourObject>() {

            @Override
            public int compare(YourObject o1, YourObject o2) {
                return o1.getName().compareTo(o2.getName());
            }
        });

컬렉션에 개체가 없으면 음수를 반환하거나 그렇지 않으면 index개체의 개체를 반환 합니다. 이를 통해 검색 전략이 다른 객체를 검색 할 수 있습니다.


구아바를 사용하지 않으려는 소규모 프로젝트에 적합한 솔루션입니다. 그러나 binarySearch의 문서에는 다음과 같은 한 가지 질문이 있습니다. 그러나 어떤 경우에는 비교 대상에 따라 그렇지 않을 수도 있습니다.
chrismarx

음수는 개체를 추가하고 다시 정렬하면 개체가 삽입되는 위치를 나타냅니다.
fiorentinoing

8

지도

당신은 만들 수 있습니다 Hashmap<String, Object>키로 값의 사용을, 그리고이라면 보는 yourHashMap.keySet().contains(yourValue)true를 반환합니다.


+1 처음에는 맵에 세부 정보를 넣는 데 약간의 오버 헤드가 발생하지만 일정한 시간 조회가 가능합니다. 지금까지 아무도 이것을 제안하지 않은 것에 놀랐습니다.
루디 커 쇼우

6

이클립스 컬렉션

당신이 사용하는 경우 이클립스 컬렉션 , 당신은 사용할 수있는 anySatisfy()방법을. 어느 당신을 적응 ListA의 ListAdapter또는 변경 ListListIterable가능합니다.

ListIterable<MyObject> list = ...;

boolean result =
    list.anySatisfy(myObject -> myObject.getName().equals("John"));

이와 같은 작업을 자주 수행하는 경우 유형에 속성이 있는지 여부를 응답하는 메소드를 추출하는 것이 좋습니다.

public class MyObject
{
    private final String name;

    public MyObject(String name)
    {
        this.name = name;
    }

    public boolean named(String name)
    {
        return Objects.equals(this.name, name);
    }
}

대체 양식을 anySatisfyWith()메소드 참조와 함께 사용할 수 있습니다 .

boolean result = list.anySatisfyWith(MyObject::named, "John");

당신이 당신을 변경할 수없는 경우 ListListIterable, 여기 당신이 사용하는 거라고 방법 ListAdapter.

boolean result = 
    ListAdapter.adapt(list).anySatisfyWith(MyObject::named, "John");

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


5

Predicate

Java 8 또는 콜렉션 처리에 더 많은 기능을 제공하는 라이브러리를 사용하지 않으면 솔루션보다 재사용 가능한 것을 구현할 수 있습니다.

interface Predicate<T>{
        boolean contains(T item);
    }

    static class CollectionUtil{

        public static <T> T find(final Collection<T> collection,final  Predicate<T> predicate){
            for (T item : collection){
                if (predicate.contains(item)){
                    return item;
                }
            }
            return null;
        }
    // and many more methods to deal with collection    
    }

나는 그런 것을 사용하고 있으며, 술어 인터페이스가 있으며 구현을 내 util 클래스에 전달하고 있습니다.

내 방식으로이 작업을 수행하면 어떤 이점이 있습니까? 모든 유형 콜렉션에서 검색을 처리하는 한 가지 방법이 있습니다. 다른 필드로 검색하려는 경우 별도의 메소드를 작성할 필요가 없습니다. alll 당신이해야 할 일은 더 이상 유용하지 않으면 즉시 파괴 될 수있는 다른 술어를 제공하는 것입니다.

사용하려면 메소드를 호출하고 술어를 정의하십시오.

CollectionUtil.find(list, new Predicate<MyObject>{
    public boolean contains(T item){
        return "John".equals(item.getName());
     }
});

4

구아바를 사용하는 솔루션은 다음과 같습니다.

private boolean checkUserListContainName(List<User> userList, final String targetName){

    return FluentIterable.from(userList).anyMatch(new Predicate<User>() {
        @Override
        public boolean apply(@Nullable User input) {
            return input.getName().equals(targetName);
        }
    });
}

3

contains방법은 equals내부적으로 사용합니다 . 따라서 필요 equals에 따라 클래스 의 메소드 를 대체 해야합니다.

Btw 이것은 구문 상 정확하지 않습니다.

new Object().setName("John")

3
세터 반환하지 않는 한 this.
Sotirios Delimanolis

따라서 목록에 추가되는 사용자 정의 객체의 equals 메서드를 재정의하면 특정 클래스 변수가 동일한 경우 true를 반환하도록 할 수 있습니까?
Rudi Kershaw

@RudiKershaw 예, 하나, 둘, 모든 필드를 기준으로 true를 반환 할 수 있습니다. 따라서 동일한 이름을 가진 두 개의 객체가 동일한 객체라고 말하는 것이 논리적으로 정확하다고 생각되면 이름을 비교하여 true를 반환합니다.
Juned Ahsan

1
equals일반적으로 클래스에 의미가 없다면 단일 사용 사례에 대해 재정의 하는 것은 바람직하지 않습니다 . 루프를 작성하는 것만으로도 훨씬 나을 것입니다.
MikeFHay

@MikeFHay 동의, 그래서 내가 의견에서 언급 한 이유는 "따라서 같은 이름을 가진 두 개의 객체가 동일한 객체라고 말하는 것이 논리적으로 옳다고 믿는다면"
Juned Ahsan

1

이 작업을 List.contains(Object with field value equal to x)반복적 으로 수행해야하는 경우 간단하고 효율적인 해결 방법은 다음과 같습니다.

List<field obj type> fieldOfInterestValues = new ArrayList<field obj type>;
for(Object obj : List) {
    fieldOfInterestValues.add(obj.getFieldOfInterest());
}

그런 다음과 List.contains(Object with field value equal to x)같은 결과가 나타납니다.fieldOfInterestValues.contains(x);


1

당신은 또한 anyMatch()사용할 수 있습니다 :

public boolean containsName(final List<MyObject> list, final String name){
    return list.stream().anyMatch(o -> o.getName().contains(name));
}

0

JAVA 8 SDK에도 불구하고 다음과 같이 작업하는 데 도움이되는 많은 수집 도구 라이브러리가 있습니다. http://commons.apache.org/proper/commons-collections/

Predicate condition = new Predicate() {
   boolean evaluate(Object obj) {
        return ((Sample)obj).myField.equals("myVal");
   }
};
List result = CollectionUtils.select( list, condition );
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.