Java Try-With-Resources 未知资源计数
Java Try-With-Resources Unknown Resource Count
我需要打开 N 个多播套接字(其中 N 来自参数列表的大小)。然后我将在一个循环中将相同的数据发送到 N 个套接字中的每一个,最后关闭每个套接字。我的问题是,如何使用 try-with-resources 块执行此操作?以下是我将如何使用单个资源执行此操作:
final int port = ...;
try (final MulticastSocket socket = new MulticastSocket(port)) {
// Do a bunch of sends of small packet data over a long period of time
...
}
我能想到的使用多个端口执行此操作的唯一方法如下:
final List<Integer> ports = ...;
final List<MulticastSocket> sockets = new ArrayList<>(ports.size());
try {
for (final Integer port : ports) {
sockets.add(new MulticastSocket(port));
}
// Do a bunch of sends of small packet data over a long period of time
...
} finally {
for (final MulticastSocket socket : sockets) {
try {
socket.close();
} catch (final Throwable t) {
// Eat the exception
}
}
}
是否有更简洁的方法来完成此任务,或者我提出的解决方案是否已达到最佳效果?
您所做的几乎是最好的。
您可以创建一个 AutoCloseable
通用多关闭器,其中包含一个 List<AutoCloseable>
并接受作为构造函数参数的 closeables 计数和要调用以创建每个 closeable 的工厂,然后当它的 close()
被调用时关闭它们,这样你就可以像这样使用它:
try( MultiCloser<MulticastSocket> multiCloser =
new MultiCloser<>( ports.size(), i -> new MulticastSocket( ports.get( i ) ) )
{
for( MulticastSocket socket : multiCloser.getItems() )
{
do something with the socket
}
}
...但这可能有点矫枉过正。
递归执行以保持 try-with-resources 的保证:
void foo(List<Integer> ports, List<Socket> sockets) {
if (sockets.size() == ports.size()) {
// Do something with your sockets.
} else {
try (Socket s = new MulticastSocket(ports.get(sockets.size())) {
sockets.add(s);
foo(ports, sockets);
// You could call sockets.remove(sockets.size()-1) here.
// Not convinced whether it's worth it.
}
}
}
使用 ArrayList
存储 MulticastSocket
实例有什么意义?
你是这么说的:
I will then send the same data to each of the N sockets within a
loop, and finally, close each socket.
因此您可以在循环中创建它们并为每次迭代发送相同的处理。
为此,您应该稍微更改一下设计。
MulticastSocket
的处理任务可以由功能接口执行,该接口还允许指定要使用的端口。
例如:
@FunctionalInterface
public interface SocketProcessor {
void process(MulticastSocket multicastSocket) ;
}
您可以有一个方法将此功能接口作为参数来应用处理:
public static void processSocket(SocketProcessor socketProcessor, Integer port) throws IOException {
try (final MulticastSocket socket = new MulticastSocket(port)) {
socketProcessor.process(socket);
}
}
最后,您可以从客户端代码创建一个带有 lambda 的 socketProcessor 实例:
SocketProcessor socketProcessor = (MulticastSocket socket) -> {
socket.send(...);
socket.send(...);
};
然后您可以在端口上循环,以便使用合适的端口和刚刚创建的 SocketProcessor
实例调用 processSocket :
for (final Integer port : ports) {
try {
processSocket(socketProcessor, port);
} catch (IOException e) {
// do processing
}
}
这个解决方案不一定要更短(实际上不会更长),但它确实更清晰。
两个主要问题是分开的:
processSocket(SocketProcessor)
执行样板代码
SocketProcessor
定义具体任务。
受到 Mike Nakis 提出的想法的启发,我想出了以下 class...
package myNamespace;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import myNamespace.ThrowingFunction;
import myNamespace.ThrowingSupplier;
/** Collection of AutoCloseable objects */
public class ResourceCollection<T extends AutoCloseable>
implements Iterable<T>, AutoCloseable {
/** Resources owned by this instance */
private final List<T> myResources;
/**
* Constructor
* @param allocator Function used to allocate each resource
* @param count Number of times to call the allocator
* @throws E Thrown if any of the allocators throw
*/
public <E extends Throwable> ResourceCollection(
final ThrowingSupplier<T, E> allocator, final int count)
throws E {
myResources = new ArrayList<>(count);
try {
while (myResources.size() < count) {
final T resource = allocator.getThrows();
myResources.add(resource);
}
} catch (final Throwable e) {
close();
throw e;
}
}
/**
* Constructor
* @param allocator Function used to allocate each resource
* @param input List of input parameters passed to the allocator
* @throws E Thrown if any of the allocators throw
*/
public <U, E extends Throwable> ResourceCollection(
final ThrowingFunction<U, T, E> allocator, final Collection<U> input)
throws E {
myResources = new ArrayList<>(input.size());
try {
for (final U value : input) {
final T resource = allocator.applyThrows(value);
myResources.add(resource);
}
} catch (final Throwable e) {
close();
throw e;
}
}
/**
* Gets the number of resources in the collection
* @return The number of resources in the collection
*/
public int size() {
return myResources.size();
}
/**
* Gets whether the collection contains no resources
* @return Whether the collection contains no resources
*/
public boolean isEmpty() {
return myResources.isEmpty();
}
/**
* Gets the resource at index i
* @param i The index of a resource, in the range [0, size())
* @return The resource at index i
*/
public T get(final int i) {
return myResources.get(i);
}
@Override
public Iterator<T> iterator() {
return myResources.iterator();
}
@Override
public void close() {
final ListIterator<T> resourceIter =
myResources.listIterator(myResources.size());
while (resourceIter.hasPrevious()) {
final T resource = resourceIter.previous();
if (resource != null) {
try {
resource .close ();
resourceIter.remove();
} catch (final Throwable t) {
// Eat the exception
}
}
}
}
}
我需要打开 N 个多播套接字(其中 N 来自参数列表的大小)。然后我将在一个循环中将相同的数据发送到 N 个套接字中的每一个,最后关闭每个套接字。我的问题是,如何使用 try-with-resources 块执行此操作?以下是我将如何使用单个资源执行此操作:
final int port = ...;
try (final MulticastSocket socket = new MulticastSocket(port)) {
// Do a bunch of sends of small packet data over a long period of time
...
}
我能想到的使用多个端口执行此操作的唯一方法如下:
final List<Integer> ports = ...;
final List<MulticastSocket> sockets = new ArrayList<>(ports.size());
try {
for (final Integer port : ports) {
sockets.add(new MulticastSocket(port));
}
// Do a bunch of sends of small packet data over a long period of time
...
} finally {
for (final MulticastSocket socket : sockets) {
try {
socket.close();
} catch (final Throwable t) {
// Eat the exception
}
}
}
是否有更简洁的方法来完成此任务,或者我提出的解决方案是否已达到最佳效果?
您所做的几乎是最好的。
您可以创建一个 AutoCloseable
通用多关闭器,其中包含一个 List<AutoCloseable>
并接受作为构造函数参数的 closeables 计数和要调用以创建每个 closeable 的工厂,然后当它的 close()
被调用时关闭它们,这样你就可以像这样使用它:
try( MultiCloser<MulticastSocket> multiCloser =
new MultiCloser<>( ports.size(), i -> new MulticastSocket( ports.get( i ) ) )
{
for( MulticastSocket socket : multiCloser.getItems() )
{
do something with the socket
}
}
...但这可能有点矫枉过正。
递归执行以保持 try-with-resources 的保证:
void foo(List<Integer> ports, List<Socket> sockets) {
if (sockets.size() == ports.size()) {
// Do something with your sockets.
} else {
try (Socket s = new MulticastSocket(ports.get(sockets.size())) {
sockets.add(s);
foo(ports, sockets);
// You could call sockets.remove(sockets.size()-1) here.
// Not convinced whether it's worth it.
}
}
}
使用 ArrayList
存储 MulticastSocket
实例有什么意义?
你是这么说的:
I will then send the same data to each of the N sockets within a loop, and finally, close each socket.
因此您可以在循环中创建它们并为每次迭代发送相同的处理。
为此,您应该稍微更改一下设计。
MulticastSocket
的处理任务可以由功能接口执行,该接口还允许指定要使用的端口。
例如:
@FunctionalInterface
public interface SocketProcessor {
void process(MulticastSocket multicastSocket) ;
}
您可以有一个方法将此功能接口作为参数来应用处理:
public static void processSocket(SocketProcessor socketProcessor, Integer port) throws IOException {
try (final MulticastSocket socket = new MulticastSocket(port)) {
socketProcessor.process(socket);
}
}
最后,您可以从客户端代码创建一个带有 lambda 的 socketProcessor 实例:
SocketProcessor socketProcessor = (MulticastSocket socket) -> {
socket.send(...);
socket.send(...);
};
然后您可以在端口上循环,以便使用合适的端口和刚刚创建的 SocketProcessor
实例调用 processSocket :
for (final Integer port : ports) {
try {
processSocket(socketProcessor, port);
} catch (IOException e) {
// do processing
}
}
这个解决方案不一定要更短(实际上不会更长),但它确实更清晰。
两个主要问题是分开的:
processSocket(SocketProcessor)
执行样板代码SocketProcessor
定义具体任务。
受到 Mike Nakis 提出的想法的启发,我想出了以下 class...
package myNamespace;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import myNamespace.ThrowingFunction;
import myNamespace.ThrowingSupplier;
/** Collection of AutoCloseable objects */
public class ResourceCollection<T extends AutoCloseable>
implements Iterable<T>, AutoCloseable {
/** Resources owned by this instance */
private final List<T> myResources;
/**
* Constructor
* @param allocator Function used to allocate each resource
* @param count Number of times to call the allocator
* @throws E Thrown if any of the allocators throw
*/
public <E extends Throwable> ResourceCollection(
final ThrowingSupplier<T, E> allocator, final int count)
throws E {
myResources = new ArrayList<>(count);
try {
while (myResources.size() < count) {
final T resource = allocator.getThrows();
myResources.add(resource);
}
} catch (final Throwable e) {
close();
throw e;
}
}
/**
* Constructor
* @param allocator Function used to allocate each resource
* @param input List of input parameters passed to the allocator
* @throws E Thrown if any of the allocators throw
*/
public <U, E extends Throwable> ResourceCollection(
final ThrowingFunction<U, T, E> allocator, final Collection<U> input)
throws E {
myResources = new ArrayList<>(input.size());
try {
for (final U value : input) {
final T resource = allocator.applyThrows(value);
myResources.add(resource);
}
} catch (final Throwable e) {
close();
throw e;
}
}
/**
* Gets the number of resources in the collection
* @return The number of resources in the collection
*/
public int size() {
return myResources.size();
}
/**
* Gets whether the collection contains no resources
* @return Whether the collection contains no resources
*/
public boolean isEmpty() {
return myResources.isEmpty();
}
/**
* Gets the resource at index i
* @param i The index of a resource, in the range [0, size())
* @return The resource at index i
*/
public T get(final int i) {
return myResources.get(i);
}
@Override
public Iterator<T> iterator() {
return myResources.iterator();
}
@Override
public void close() {
final ListIterator<T> resourceIter =
myResources.listIterator(myResources.size());
while (resourceIter.hasPrevious()) {
final T resource = resourceIter.previous();
if (resource != null) {
try {
resource .close ();
resourceIter.remove();
} catch (final Throwable t) {
// Eat the exception
}
}
}
}
}