English 中文(简体)
Filter Javable to 1 and only 1 elements
原标题:Filter Java Stream to 1 and only 1 element

我正试图利用Java 8 Streams,以便找到中的内容。 然而,我要保证,与过滤标准相匹配只有一个,只有一个。

采用该守则:

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 match = users.stream().filter((user) -> user.getId() < 0).findAny().get();

扔下一个<代码>NoSuchElementException(正!)

不过,如果存在多种对应措施,我会错。 是否有办法这样做?

最佳回答

Create a custom 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);
            }
    );
}

我们使用, 用于构建我们所希望的、Collector

  1. Collecting our objects in a List with the Collectors.toList() collector.
  2. Applying an extra finisher at the end, that returns the single element — or throws an IllegalStateException if list.size != 1.

用于:

User resultUser = users.stream()
        .filter(user -> user.getId() > 0)
        .collect(toSingleton());

You can then customize this Collector as much as you want, for example give the exception as argument in the constructor, tweak it to allow two values, and more.

An alternative — arguably less elegant — solution:

您可以采用一种工作方式,即:peek()和AtomicInteger,但事实上,你应当使用。

What you could do instead is just collecting it in a List, like this:

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 in case the stream is empty, or
  • IllegalStateException in case the stream contains more than one matching element.

这种做法的改变避免了过早放弃例外,而是作为<条码><>[>选择>/代码>的结果,该编码要么包含唯一的要素,要么包含零或多重内容的任何(豁免):

Optional<User> user1 = users.stream()
        .filter(user -> user.getId() == 1)
        .collect(Collectors.reducing((a, b) -> null));

Guava provides MoreCollectors.onlyElement() which does the right thing here. But if you have to do it yourself, you could roll your own Collector for this:

<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。 你可以像你一样重新使用<代码>。

“替罪羊”行动,让你去除那些没有其他途径得到溪流支持的物品,就是要求建立<条码>。 编号:

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

Guava有一个方便的方法,可以采用Iterator,并取得唯一的要素,如果存在零或多重内容,可取代底线1。

Update

@Holger的评论中的尼斯建议:

Optional<User> match = users.stream()
              .filter((user) -> user.getId() > 1)
              .reduce((u, v) -> { throw new IllegalStateException("More than one ID found") });

Original answer

这一例外被<代码>Optional#get/code>所推翻,但如果你有一个以上的要素赢得一定帮助的话。 您可以收集用户,收集仅接受一个项目的收集资料,例如:

User match = users.stream().filter((user) -> user.getId() > 1)
                  .collect(toCollection(() -> new ArrayBlockingQueue<User>(1)))
                  .poll();

交存<条码>java.lang.IllegalStateException: 查阅全程,但这感到太 ha。

或者你可以使用与选择相结合的削减办法:

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 if no user is found
  • the user if only one is found
  • throws an exception if more than one is found

The result is then wrapped in an optional.

但是,最简单的解决办法可能是仅仅收集到收集,检查其规模为1,并掌握唯一的要素。

I think this way is more simple:

User resultUser = users.stream()
    .filter(user -> user.getId() > 0)
    .findFirst().get();

Using reduce

这是我发现的更为简单和灵活的方式(基于“@答案”)。

Optional<User> user = users.stream()
        .filter(user -> user.getId() == 1)
        .reduce((a, b) -> {
            throw new IllegalStateException("Multiple elements: " + a + ", " + b);
        })

这样,你就可以:

  • the Optional - as always with your object or Optional.empty() if not present
  • the Exception (with eventually YOUR custom type/message) if there s more than one element

An alternative is to use reduction: (this example uses strings but could easily apply to any object type including User)

List<String> list = ImmutableList.of("one", "two", "three", "four", "five", "two");
String match = list.stream().filter("two"::equals).reduce(thereCanBeOnlyOne()).get();
//throws NoSuchElementException if there are no matching elements - "zero"
//throws RuntimeException if duplicates are found - "two"
//otherwise returns the match - "one"
...

//Reduction operator that throws RuntimeException if there are duplicates
private static <T> BinaryOperator<T> thereCanBeOnlyOne()
{
    return (a, b) -> {throw new RuntimeException("Duplicate elements found: " + a + " and " + b);};
}

因此,请登录<代码>。 用户

User match = users.stream().filter((user) -> user.getId() < 0).reduce(thereCanBeOnlyOne()).get();

Using a Collector:

public static <T> Collector<T, ?, Optional<T>> singleElementCollector() {
    return Collectors.collectingAndThen(
            Collectors.toList(),
            list -> list.size() == 1 ? Optional.of(list.get(0)) : Optional.empty()
    );
}

Usage:

Optional<User> result = users.stream()
        .filter((user) -> user.getId() < 0)
        .collect(singleElementCollector());

我们回去了一个Optional,因为我们通常可以假定<>Collection含有一个内容。 如果你已经知道这一点,请:

User user = result.orElseThrow();

这就给打电话者带来负担——这是应当的。

<>Using Reduce and Optional

https://stackoverflow.com/a/52006240/6136489”

public <T> T getOneExample(Collection<T> collection) {
    return collection.stream()
        .filter(x -> /* do some filter */)
        .reduce((x,y)-> {throw new IllegalStateException("multiple");})
        .orElseThrow(() -> new NoSuchElementException("none"));
}

作为Collectors.toMap(keyMapper, ValueMapper),利用 throw合并处理具有相同关键意义的多个条目:

List<User> users = new LinkedList<>();
users.add(new User(1, "User1"));
users.add(new User(2, "User2"));
users.add(new User(3, "User3"));

int id = 1;
User match = Optional.ofNullable(users.stream()
  .filter(user -> user.getId() == id)
  .collect(Collectors.toMap(User::getId, Function.identity()))
  .get(id)).get();

您将获得关于重复钥匙的。 但最后,我无法确定,如果使用<条码><>>>><>条/条码>,该代码将更无法阅读。

我正在利用这两名收集者:

public static <T> Collector<T, ?, Optional<T>> zeroOrOne() {
    return Collectors.reducing((a, b) -> {
        throw new IllegalStateException("More than one value was returned");
    });
}

public static <T> Collector<T, ?, T> onlyOne() {
    return Collectors.collectingAndThen(zeroOrOne(), Optional::get);
}

http://static.javadoc.io/com.aol。

singleOptional() throws an exception if there are 0 or more than 1 elements in the Stream, otherwise it returns the single value.

String result = SequenceM.of("x")
                          .single();

SequenceM.of().single(); // NoSuchElementException

SequenceM.of(1, 2, 3).single(); // NoSuchElementException

String result = LazyFutureStream.fromStream(Stream.of("x"))
                          .single();

singleOptional() returns Optional.empty() if there are no values or more than one value in the Stream.

Optional<String> result = SequenceM.fromStream(Stream.of("x"))
                          .singleOptional(); 
//Optional["x"]

Optional<String> result = SequenceM.of().singleOptional(); 
// Optional.empty

Optional<String> result =  SequenceM.of(1, 2, 3).singleOptional(); 
// Optional.empty

披露——我是这两个图书馆的作者。

 List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
Integer value  = list.stream().filter((x->x.intValue()==8)).findFirst().orElse(null);

I 使用了Integer类型,而不是原始,因为它将具有无效例外。 你只是要处理这一例外......我想简明扼要;)

Tried a sample code for my self and here is the solution for that.

User user = Stream.of(new User(2), new User(2), new User(1), new User(2))
            .filter(u -> u.getAge() == 2).findFirst().get();

和用户类别

class User {
    private int age;

public User(int age) {
    this.age = age;
}

public int getAge() {
    return age;
}

public void setAge(int age) {
    this.age = age;
 }
}
public List<state> getAllActiveState() {
    List<Master> master = masterRepository.getActiveExamMasters();
    Master activeMaster = new Master();
    try {
        activeMaster = master.stream().filter(status -> status.getStatus() == true).reduce((u, v) -> {
            throw new IllegalStateException();
        }).get();
        return stateRepository.getAllStateActiveId(activeMaster.getId());
    } catch (IllegalStateException e) {
        logger.info(":More than one status found TRUE in Master");
        return null;
    }
}
  1. In this above code, As per the condition if its find more than one true in the list then it will through the exception.
  2. When it through the error will showing custom message because it easy maintain the logs on server side.
  3. From Nth number of element present in list just want only one element have true condition if in list there are more than one elements having true status at that moment it will through an exception.
  4. after getting all the this we using get(); to taking that one element from list and stored it into another object.
  5. If you want you added optional like Optional<activeMaster > = master.stream().filter(status -> status.getStatus() == true).reduce((u, v) -> {throw new IllegalStateException();}).get();
User match = users.stream().filter((user) -> user.getId()== 1).findAny().orElseThrow(()-> new IllegalArgumentException());

在“@skiwi”的激励下,我解决了以下问题:

public static <T> T toSingleton(Stream<T> stream) {
    List<T> list = stream.limit(1).collect(Collectors.toList());
    if (list.isEmpty()) {
        return null;
    } else {
        return list.get(0);
    }
}

之后:

User user = toSingleton(users.stream().filter(...).map(...));




相关问题
Spring Properties File

Hi have this j2ee web application developed using spring framework. I have a problem with rendering mnessages in nihongo characters from the properties file. I tried converting the file to ascii using ...

Logging a global ID in multiple components

I have a system which contains multiple applications connected together using JMS and Spring Integration. Messages get sent along a chain of applications. [App A] -> [App B] -> [App C] We set a ...

Java Library Size

If I m given two Java Libraries in Jar format, 1 having no bells and whistles, and the other having lots of them that will mostly go unused.... my question is: How will the larger, mostly unused ...

How to get the Array Class for a given Class in Java?

I have a Class variable that holds a certain type and I need to get a variable that holds the corresponding array class. The best I could come up with is this: Class arrayOfFooClass = java.lang....

SQLite , Derby vs file system

I m working on a Java desktop application that reads and writes from/to different files. I think a better solution would be to replace the file system by a SQLite database. How hard is it to migrate ...

热门标签