Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shortest way to iterate over non-list?

Tags:

java

java-8

Suppose I have 3 Scanner instances which I want to close.

I could do

sc.close() 

for each of the Scanners.

Or I could do something like

for (Scanner sc: new Scanner[]{sc1,sc2,sc3}) {     sc.close(); } 

Is there any shorter way of doing this with Java 8?

Something similar to?

{sc1,sc2,sc3}.forEach((sc) -> sc.close()); 
like image 883
E. Sundin Avatar asked May 04 '16 09:05

E. Sundin


2 Answers

Since Java 7 you should use try-with-resources

try(Scanner sc1 = new Scanner("");      Scanner sc2 = new Scanner("");      Scanner sc3 = new Scanner("")){  } //all scanners get closed implicitly 

So you don't need any code at all.

The problem with all for-each or stream constructs is, that - theoretically - if the first close() fails with an exception on invoking the underlying source's close() method, the following scanners won't get closed. The Scanner.close() implementation catches any IOException, but no other exception that may occur.

The try-with-resources construct deals with that, the loops don't.


EDIT: While your question targeted at a more general approach, the above solution was a response to your particular problem: to deal with AutoCloseable resources, which should in any case be used with the try-with-resources construct, requiring no special treatment of the close method at all (= shortest solution to your particular problem).

Regarding the more general question about dealing with arbitrary items (which are no resources), Java has at least two options:

Creating a List from an Array/Varargs and iterate over it

for(YourItemType item : Arrays.asList(your,items,here)) {   //do something } 

Creating a Stream from an Array/Varargs and apply functions to it

Stream.of(your,items,here).forEach(item -> { doSomething}); 

Of course the "doSomething" can be replaced with a method reference

Stream.of(your,items,here).forEach(this::myMethod); ... void myMethod(YourItemType item){   //doSomething }  

The problem with that approach is, that checked exception have to be dealt with explicitly in lambda expressions. Lets take the above example and let myMethod throw a checked exception

void myMethod(YourItemType item) throws Exception 

in that case your stream statement would have to look like

Stream.of(your,items,here).forEach(item -> {   try {     myMethod(item);   } catch (Exception e){     //omit or throw new RuntimeException(e);   }; 

This doesn't look that nice. But we could put the lambda body in a separate method

void myMethodQuietly(YourItemType item) {   try {     myMethod(item);   }catch(Exception e){     //omit or throw new RuntimeException(e);   } }  Stream.of(your,items,here).forEach(this::myMethodQuietly); 

This approach may be of interest to your particular resource problem. We can put all this into a CompositeAutoCloseable that takes resources created outside the class that all should be closed safely on invocation of close()

public class CompositeAutoCloseable implements AutoCloseable {    private List<Closeable> resources;    public CompositeAutoCloseable(Closeable... resources) {     this.resources = Arrays.asList(resources);     //you could use a stream here too   }    @Override   public void close() {       this.resources.stream().forEach(this::closeQuietly);   }    void closeQuietly(Closeable res) {     if(res == null)  {         return;     }     try {         res.close();     }catch(Exception e){         //omit     }   }  } 

And once you have such a helper class, you can use it again with try-with-resources.

try(CompositeAutoCloseable cac = new CompositeAutoCloseable(sc1,sc2,sc3)) {   //do something } 

I leave it up to you to decide if this makes sense compared to the inital solution ;)

like image 62
Gerald Mücke Avatar answered Oct 07 '22 10:10

Gerald Mücke


If instantiation is separated from disposal, use Guava's com.google.common.io.Closer.

like image 27
Basilevs Avatar answered Oct 07 '22 09:10

Basilevs