Java 8을 사용 Stream
하여의 요소를 찾으려고합니다 LinkedList
. 그러나 필터 기준과 일치하는 항목이 하나만 있음을 보증하고 싶습니다.
이 코드를 보자 :
public static void main(String[] args) {
LinkedList<User> users = new LinkedList<>();
users.add(new User(1, "User1"));
users.add(new User(2, "User2"));
users.add(new User(3, "User3"));
User match = users.stream().filter((user) -> user.getId() == 1).findAny().get();
System.out.println(match.toString());
}
static class User {
@Override
public String toString() {
return id + " - " + username;
}
int id;
String username;
public User() {
}
public User(int id, String username) {
this.id = id;
this.username = username;
}
public void setUsername(String username) {
this.username = username;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public int getId() {
return id;
}
}
이 코드는 User
ID를 기반으로 찾습니다 . 그러나 User
필터와 일치 하는 수를 보장 할 수는 없습니다 .
필터 라인을 다음으로 변경 :
User match = users.stream().filter((user) -> user.getId() < 0).findAny().get();
던질 것입니다 NoSuchElementException
(좋은!)
그래도 여러 개의 일치 항목이 있으면 오류가 발생하도록하고 싶습니다. 이 방법이 있습니까?
답변
맞춤 만들기 Collector
public static <T> Collector<T, ?, T> toSingleton() {
return Collectors.collectingAndThen(
Collectors.toList(),
list -> {
if (list.size() != 1) {
throw new IllegalStateException();
}
return list.get(0);
}
);
}
우리가 사용하는 Collectors.collectingAndThen
우리의 원하는 구성하기 Collector
로를
- 수집기를
List
사용하여 객체를Collectors.toList()
수집합니다. - 끝에 추가 피니셔를 적용하면 단일 요소가 반환되거나
IllegalStateException
if 가 발생list.size != 1
합니다.
로 사용 :
User resultUser = users.stream()
.filter(user -> user.getId() > 0)
.collect(toSingleton());
그런 다음 원하는대로 이것을 사용자 정의 할 수 있습니다. Collector
예를 들어, 예외를 생성자에서 인수로 지정하고 두 값을 허용하도록 조정하십시오.
대체로 우아하지 않은 대안 :
당신은 관련된 ‘해결’사용 peek()
과를 AtomicInteger
하지만, 정말 당신은을 사용해서는 안됩니다.
당신이 할 수있는 일은 다음 List
과 같이 그것을 수집하는 것입니다 .
LinkedList<User> users = new LinkedList<>();
users.add(new User(1, "User1"));
users.add(new User(2, "User2"));
users.add(new User(3, "User3"));
List<User> resultUserList = users.stream()
.filter(user -> user.getId() == 1)
.collect(Collectors.toList());
if (resultUserList.size() != 1) {
throw new IllegalStateException();
}
User resultUser = resultUserList.get(0);
답변
완전성을 위해 다음은 @prunge의 탁월한 답변에 해당하는 ‘한 줄짜리’입니다.
User user1 = users.stream()
.filter(user -> user.getId() == 1)
.reduce((a, b) -> {
throw new IllegalStateException("Multiple elements: " + a + ", " + b);
})
.get();
이것은 스트림에서 유일한 일치하는 요소를 가져 와서
NoSuchElementException
스트림이 비어있는 경우IllegalStateException
스트림에 둘 이상의 일치하는 요소가 포함 된 경우
이 접근법의 변형은 예외를 조기에 던지는 것을 피하고 대신 Optional
단독 요소를 포함하거나 0 또는 다중 요소가있는 경우 아무것도 없음 (빈)으로 결과를 나타냅니다 .
Optional<User> user1 = users.stream()
.filter(user -> user.getId() == 1)
.collect(Collectors.reducing((a, b) -> null));
답변
사용자 정의 작성과 관련된 다른 대답 Collector
은 아마도 더 효율적일 것입니다 (예 : Louis Wasserman ‘s , +1). 간결성을 원한다면 다음을 제안합니다.
List<User> result = users.stream()
.filter(user -> user.getId() == 1)
.limit(2)
.collect(Collectors.toList());
그런 다음 결과 목록의 크기를 확인하십시오.
if (result.size() != 1) {
throw new IllegalStateException("Expected exactly one user but got " + result);
User user = result.get(0);
}
답변
구아바 는 MoreCollectors.onlyElement()
여기서 옳은 일을합니다. 그러나 직접 해야하는 경우이 작업을 수행 할 수 있습니다 Collector
.
<E> Collector<E, ?, Optional<E>> getOnly() {
return Collector.of(
AtomicReference::new,
(ref, e) -> {
if (!ref.compareAndSet(null, e)) {
throw new IllegalArgumentException("Multiple values");
}
},
(ref1, ref2) -> {
if (ref1.get() == null) {
return ref2;
} else if (ref2.get() != null) {
throw new IllegalArgumentException("Multiple values");
} else {
return ref1;
}
},
ref -> Optional.ofNullable(ref.get()),
Collector.Characteristics.UNORDERED);
}
… 또는 Holder
대신 자신의 유형을 사용합니다 AtomicReference
. 원하는 Collector
만큼 재사용 할 수 있습니다 .
답변
구아바 MoreCollectors.onlyElement()
( JavaDoc )를 사용하십시오 .
IllegalArgumentException
스트림이 둘 이상의 요소로 구성되어 NoSuchElementException
있고 스트림이 비어 있으면 원하는 것을 수행하고 throw합니다 .
용법:
import static com.google.common.collect.MoreCollectors.onlyElement;
User match =
users.stream().filter((user) -> user.getId() < 0).collect(onlyElement());
답변
스트림에서 지원하지 않는 이상한 작업을 수행 할 수있는 “이스케이프 해치”작업은 다음을 요청하는 것입니다 Iterator
.
Iterator<T> it = users.stream().filter((user) -> user.getId() < 0).iterator();
if (!it.hasNext())
throw new NoSuchElementException();
else {
result = it.next();
if (it.hasNext())
throw new TooManyElementsException();
}
구아바는 Iterator
유일한 요소 를 가져 와서 유일한 요소 를 가져 와서 0 개 또는 여러 개의 요소가있는 경우 던지는데 여기에서 맨 아래 n-1 줄을 바꿀 수 있습니다.
답변
최신 정보
@Holger의 의견에 대한 좋은 제안 :
Optional<User> match = users.stream()
.filter((user) -> user.getId() > 1)
.reduce((u, v) -> { throw new IllegalStateException("More than one ID found") });
원래 답변
예외는에 의해 발생 Optional#get
하지만 도움이되지 않는 요소가 두 개 이상인 경우. 하나의 항목 만 허용하는 컬렉션의 사용자를 수집 할 수 있습니다 (예 :
User match = users.stream().filter((user) -> user.getId() > 1)
.collect(toCollection(() -> new ArrayBlockingQueue<User>(1)))
.poll();
을 던지지 java.lang.IllegalStateException: Queue full
만 너무 해키 느낌.
또는 옵션과 함께 축소를 사용할 수 있습니다.
User match = Optional.ofNullable(users.stream().filter((user) -> user.getId() > 1)
.reduce(null, (u, v) -> {
if (u != null && v != null)
throw new IllegalStateException("More than one ID found");
else return u == null ? v : u;
})).get();
감소는 본질적으로 다음을 반환합니다.
- 사용자가 없으면 null
- 하나만 발견되면 사용자
- 둘 이상이 발견되면 예외가 발생합니다.
그런 다음 결과는 선택 사항으로 래핑됩니다.
그러나 가장 간단한 해결책은 아마도 컬렉션으로 수집하고 크기가 1인지 확인하고 유일한 요소를 얻는 것입니다.