我可以在流式传输集合时向集合添加新对象吗?



我有一个Set<Client>,我正在用数据库查询的结果填充它。生成的行包含客户端的不同值,以便多行包含要添加到单个Client实例的值。
Client存储在上述Set<Client>中,目前,我正在检查客户端是否已经存在于该Set中或需要由以创建:

Set<Client> clients = new TreeSet<>(); 
// yes, Client implements Comparable<Client>, has hashCode() and equals()
// some things that are irrelevant to this question happen here
// then iterating the database result
ResultSet rs = ps.executeQuery();
while (rs.next()) {
int clientNumber = rs.getInt(...);
String clientName = rs.getString(...);
// get the client from the collection or create a new one if it isn't there
Client client = clients.stream()
.filter(c -> c.getNumber() == clientNumber)
.findFirst()
.orElse(new Client(clientNumber, clientName));
// here I have to check again if the client is in the Set, how can I avoid that?
}

但是,这当然不会将可能新创建的Client添加到Set<Client>中,之后我需要再次检查clients是否包含此实例,以确定我是否必须将此实例添加到clients。我甚至不能尝试安提,因为我甚至不能添加Stream的方法,比如concat(...)clients.stream()...语句中的任何位置。

这就引出了以下问题:

有没有办法提供在流式传输此Set时直接将Client(或任何对象(的新实例添加到Set的可能性,并且新实例由.orElse(new ...)创建?

我可以想象像orElseAdd(new ...)这样的东西,但找不到类似的东西。

Map提供了一种有用的方法,如果它不存在,则插入某些内容。

将客户端存储在地图中也没有什么坏处。从您的示例中可以看出,clientNumber是独一无二的,因此这将是关键。

Map<Integer, Client> clients = new TreeMap<>();
...
Client client = clients.computeIfAbsent(clientNumber, key -> new Client(clientNumber, clientName));

当执行 lambda 时,key等于clientNumber,因此可以忽略它。请注意,lambda仅在客户端不在映射中时才执行,因此不必担心创建太多对象。这并不重要,因为如果clientNumber大于缓存阈值(在我的 VM 中为 255(,它还会创建盒装Integer对象。幸运的是,Java在处理短期对象时非常有效。

如果创建客户端的过程足够轻,不用担心创建 dup,您可以让 Set 为您控制它。

Set<Client> clients = new TreeSet<>(); 
ResultSet rs = ps.executeQuery();
while (rs.next()) {
int clientNumber = rs.getInt(...);
String clientName = rs.getString(...);
clients.add(new Client(clientNumber, clientName))
}

有什么方法可以提供直接添加新实例的可能性......到片场?

现在看到这个:

例如,通常不允许一个线程修改集合,而另一个线程正在迭代它。通常,在这些情况下,迭代的结果是未定义的。如果检测到此行为,某些迭代器实现(包括 JRE 提供的所有通用集合实现的实现(可能会选择引发此异常。这样做的迭代器被称为快速失败迭代器,因为它们快速而干净地失败,而不是在未来不确定的时间冒着任意、非确定性行为的风险。

请注意,此异常并不总是指示对象已被其他线程同时修改。如果单个线程发出一系列违反对象协定的方法调用,则该对象可能会引发此异常。例如,如果线程在使用故障快速迭代器迭代集合时直接修改集合,则迭代器将引发此异常。

引用自 ConcurrentModificationException。

问题是:在迭代时添加到集合根本不是一个好主意。最有可能的是,上述异常的实例会抛给您。有关更多想法,请参阅此处。

另外,请注意,从概念上讲:您是否也希望新添加的对象也显示在该流中?如果您的 Set 已排序,并且新对象位于集合中的某个位置怎么办......那是"已经"流式传输的?!

长话短说:你打算做的是不好的做法。而是将所有新的Client 对象收集到一个单独的集合中,并在以后自行处理该对象。

请注意:这不是强制性的,但整个"流式处理"API 建议您遵循函数式编程范式。在函数式编程中,你不会修改东西,而是创造新的东西。因此,如前所述:在新集合中收集新对象,然后最后创建一个包含旧对象和新对象的新合并集。

如果您确实想使用TreeSet来维护clients的排序顺序,并且想要将不存在的客户端添加到收集clients则尝试以下操作

步骤 1

由于TreeSet保持排序顺序,因此Client对象需要实现java.lang.Comparable的compareTo((方法,如下所示:

public class Client implements Comparable<Client>{
private int clientNumber;
private String clientName;
public Client(int clientNumber, String clientName) {
this.clientNumber = clientNumber;
this.clientName = clientName;
}
@Override
public int compareTo(Client client) {
return clientNumber - client.getClientNumber();
}
// getters and setters
}

步骤 2

由于 Set 在内部处理重复项,因此只需使用

while (rs.next()) {
int clientNumber = rs.getInt(...);
String clientName = rs.getString(...);
clients.add(new Client(clientNumber, clientName));
}

步骤 3

对于列表集合流,使用以下方式筛选非现有客户端并将其添加到我们的clients收集组

while (rs.next()) {
int clientNumber = rs.getInt(...);
String clientName = rs.getString(...);
clients.addAll(Lists.newArrayList(new Client(clientNumber, clientName)).stream()
.filter(client -> !clients.contains(client))
.collect(toList()));
}

最新更新