简体   繁体   中英

Shortest way to iterate over non-list?

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

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

如果实例化与处理分开,请使用 Guava 的com.google.common.io.Closer

Although there is sometimes a correlation between the length of the code and its quality that is not a good criterion to use to choose code.

I would probably use varargs and do it something like:

private void closeScanner(Scanner... scanners) {
    // Faff around with Java 8 in here if you like.
    for (Scanner s : scanners) {
        s.close();
    }
}

    // Some code.
    closeScanner(s, t, u);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM