Lambda expressions
Small VM
Here are two features that were originally planned but that have been dropped. They could have
made it in my list so I just mention it here. Maybe they'll make it in Java 9:
I'll now go through each new features with code example for each when relevant.
Lambda expressions
One important note, if you want to try lambda expressions with java 8, you need to get the
JDK8 with lambda support here: https://jdk8.java.net/lambda/ and not the jdk 8 early
access release that is available at https://jdk8.java.net/download.html.
The point of lambda is to be able to pass some code to a method rather than objects. Until java 8,
the way to achieve this in java would be to use callbacks. The issue with callbacks is that it leads
to verbose code. Like you would write 5 lines of code when the real job is in 1 of these 5 lines.
The other scenario is when dealing with group of data to execute specific algorithm on it.
Here are few code examples of lambda expressions usage.
Inner classes
An example of what you would do today:
btn.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
}
});
With lambda in java 8 you just have:
btn.setOnAction(
event -> System.out.println("Hello World!")
);
Method references
Two examples with static and non statics methods.
Statics method:
public class Utils {
public static int compareByLength(String in, String out){
return in.length() - out.length();
}
}
public class MyClass {
public void doSomething() {
String[] args = new String[] {"microsoft","apple","linux","oracle"}
Arrays.sort(args, Utils::compareByLength);
}
}
Non statics method:
public class MyClass implements Comparable<MyObject> {
@Override
public int compareTo(MyObject obj) {return ...}
public void doSomething() {
MyObject myObject = new MyObject();
Arrays.sort(args, myObject::compareTo);
}
}
There are many other things to say about lambda, this is just a very short overview. You can
check the complete Lambda Expressions documentation if you want to learn more about it.
Remove the Permanent Generation
That is definitely a big one. The Permanent Generation (PermGen) space has completely been
removed and is kind of replaced by a new space called Metaspace.
The consequences of the PermGen removal is that obviously the PermSize and MaxPermSize
JVM arguments are ignored and you will never get a java.lang.OutOfMemoryError: PermGen
error.
However that doesn't mean you won't have to worry about the class metadata memory footprint
and it does not eliminate class and classloader memory leaks.
Most allocations for the class metadata are now allocated out of native memory and classes used
for describing the class metadata are removed. The Metaspace will dynamically re-size
depending on the application demand at runtime. There is obviously some garbage collection
involved but I don't think that there is much to worry about here unless you have a problem of
classes, classloaders memory leak or an inadequate sizing of the metaspace (as you can specify
the maximum metaspace capacity with MaxMetaspaceSize otherwise it is limited by the amount
of available native memory).
If you want to know more about this, there is an excellent article about it From PermGen to
Metaspace on java.dzone.com.
Small VM
The goal here is to have a VM which is no more than 3Mb by allowing some features to be
excluded at build time. The motivation behind this is to allow the JVM to run on small devices
which have very strict static and dynamic memory-footprint requirements.
More info is available here.
Parallel array sorting
Java 8 introduces a new API for sorting: Arrays#parallelSort.
Arrays#parallelSort uses Fork/Join framework introduced in Java 7 to assign the sorting tasks to
multiple threads available in the thread pool.
The different methods available for parallelSort are:
parallelSort(byte[] a)
parallelSort(byte[] a, int fromIndex, int toIndex)
parallelSort(char[] a)
parallelSort(char[] a, int fromIndex, int toIndex)
parallelSort(double[] a)
Given the peopleDAO.find("London") will return thousands of Person. With just one line, I can
filter that big List to keep only the Person matching Smith, Adams and Crawford. That's pretty
cool, isn't it?
Define a standard API for Base64 encoding and decoding
Everyone probably agrees that Base64 in java "core" is bit messy and proably most resort to use
the apache Base64 library (part of apache commons) instead. So this feature is simply to
implement a standard API for Base64 and to avoid people to have to use unsupported classes
such as sun.misc.BASE64Encoder and sun.misc.BASE64Decoder or apache commons I
suppose. There are also multiple implementations of Base64 in the JDK itself so that will all be
replaced by that new standard API.
New Date & Time API
The built-in date and time handling in Java is not always very convenient to use. In general, if
you have to deal with date/time, you would often resort to the use of the Joda Time library. So in
Java 8 a new built-in API has been made which is a lot more nice to use.
The current time is represented by the javax.time.Clock class. The class is abstract, so you can
not create instances of it.
Below are few code examples.
Clock clock = Clock.systemUTC(); //return the current time based on your system clock and set
to UTC.
Clock clock = Clock.systemDefaultZone(); //return time based on system clock zone
long time = clock.millis(); //time in milliseconds from January 1st, 1970
You'll deal with timezones by using the javax.time.ZoneId class.
ZoneId zone = ZoneId.systemDefault(); //get the ZoneId of the system
Clock clock = Clock.system(zone); //set the Clock to given zone
ZoneId zone = ZoneId.of("Europe/Berlin"); //get the ZoneId from timezone name
To deal with "human" date and time, you'll use LocalDate, LocalTime and LocalDateTime.
LocalDate date = LocalDate.now();
String year = date.getYear();
String month = date.getMonthValue();
String day = date.getDayOfMonth();
To do calculations with dates, it is also very easy. Probably the best improvement compared to
the current situation with Java < 1.8
Period p = Period.of(2, HOURS);
LocalTime time = LocalTime.now();
LocalTime newTime = time.plus(p); // or time.plus(5, HOURS); or time.plusHours(5);
Provide stronger Password-Based-Encryption (PBE) algorithm implementations in the SunJCE
provider
In very short, today Java PBE algorithms from the SunJCE provider only cover DESede, and
RC2 (40-bit) with SHA1. In java 8, PBE algorithm implementations with stronger cipher and
message digest algorithms, such as AES cipher and SHA-2 family message digests, as well as
those specified by PKCS#12 are added. That means the following are added:
PBEwithSHA1AndRC4_128
PBEwithSHA1AndRC4_40
PBEwithSHA1AndDESede (2-key)
PBEwithSHA1AndRC2_128
PBEWithHmacSHA1 mac
PBEWithHmacSHA224 mac
PBEWithHmacSHA256 mac
PBEWithHmacSHA384 mac
PBEWithHmacSHA512 mac
Note that I'm using Java 1.8 (b84) which is the latest build available at the time I write this
article. I use a Thinkpad E520. I run each test 5 times with each JDK and give the average for
each. I set the heap size to 4Gb (-Xms4g -Xmx4g)
Here are the results:
Java 1.6
Java 1.7
Java 1.8
Test 1
3564ms
3653ms
3614ms
Test 2
27265ms
28773ms
28326ms
Test 3
6220ms
6579ms
6231ms
Test 4
408ms
428ms
423ms
Test 4 (parallelSort)
193ms
This concludes this article. The performance of Java 1.8 vs Java 1.7 vs Java 1.6 is not that much
different except for the parallelSort of Arrays obviously. I am a little surprised by Java 1.6
performing better and I might investigate this further, in which case I will update this post. That
could be due to GCs (G1 for Java 1.7, 1.8 vs ParallelGC for Java 1.6).
I have covered in this post what I believe are the most interesting new features of Java 1.8. There
is a lot more to come with Java 9 and 10 but that will be in quite a while. Don't hesitate to leave a
comment.
Oracle has two products that implement Java Platform Standard Edition (Java SE) 8: Java SE
Development Kit (JDK) 8 and Java SE Runtime Environment (JRE) 8.
JDK 8 is a superset of JRE 8, and contains everything that is in JRE 8, plus tools such as the
compilers and debuggers necessary for developing applets and applications. JRE 8 provides the
libraries, the Java Virtual Machine (JVM), and other components to run applets and applications
written in the Java programming language. Note that the JRE includes components not required
by the Java SE specification, including both standard and non-standard Java components.
The following conceptual diagram illustrates the components of Oracle's Java SE products:
Description of Java Conceptual Diagram
Java
Language
JD
K
Tools &
Tool APIs
Java Language
javad
oc
JCons
ole
Visual
VM
JMC
JFR
JPDA JVM TI
IDL
RMI
Java
DB
Deploy
ment
Internationaliz
ation
Web Services
java
javac
Secur
ity
Monito
ring
jar
javap Scriptin
g
Troubleshooting
Deployment
User Interface
Toolkits
Integration
Libraries
J
R
E
Other Base
Libraries
Java Virtual
Machine
Blog
Swing
Java 2D
AWT
Drag and
Drop
Input
Methods
Image
I/O
Accessibility
Print
Service
Sou
nd
Serializati
Extension
on
Mechanism
Override
Java
XML JAXP Networkin
g
Mechanism
Comp SE
Date
and
Input/Out
Internationalizati
JNI
act API
Time
put
on
Profil
lang and util
es
Collectio
Regular
Math
Ref Objects
ns
Expressions
Loggin Manage Instrument
Concurrency
g
ment
ation
Utilities
Reflecti Versionin Preferences
JAR
Zip
on
g
API
Security
About
What we do
Portfolio
People
Blog
Contact
You are viewing a single entry. Please also check out the most recent entries.
March 26, 2013
Everything about Java 8
This post was last updated on March 27, 2014.
The following post is a comprehensive summary of the developer-facing changes coming in Java 8.
As of March 18, 2014, Java 8 is now generally available.
I used preview builds of IntelliJ for my IDE. It had the best support for the Java 8 language features
at the time I went looking. You can find those builds here: IntelliJIDEA EAP.
Interface improvements
Interfaces can now define static methods. For instance, a naturalOrder method was added to
java.util.Comparator:
public static <T extends Comparable<? super T>>
Comparator<T> naturalOrder() {
return (Comparator<T>)
Comparators.NaturalOrderComparator.INSTANCE;
}
A common scenario in Java libraries is, for some interface Foo, there would be a companion utility
class Foos with static methods for generating or working with Foo instances. Now that static
methods can exist on interfaces, in many cases the Foos utility class can go away (or be made
package-private), with its public methods going on the interface instead.
Additionally, more importantly, interfaces can now define default methods. For instance, a forEach
method was added to java.lang.Iterable:
public default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
In the past it was essentially impossible for Java libraries to add methods to interfaces. Adding a
method to an interface would mean breaking all existing code that implements the interface. Now,
as long as a sensible default implementation of a method can be provided, library maintainers can
add methods to these interfaces.
In Java 8, a large number of default methods have been added to core JDK interfaces. I'll discuss
many of them later.
Why can't default methods override equals, hashCode, and toString?
An interface cannot provide a default implementation for any of the methods of the Object class. In
particular, this means one cannot provide a default implementation for equals, hashCode, or
toString from within an interface.
This seems odd at first, given that some interfaces actually define their equals behavior in
documentation. The List interface is an example. So, why not allow this?
Brian Goetz gave four reasons in a lengthy response on the Project Lambda mailing list. I'll only
describe one here, because that one was enough to convince me:
It would become more difficult to reason about when a default method is invoked. Right now it's
simple: if a class implements a method, that always wins over a default implementation. Since all
instances of interfaces are Objects, all instances of interfaces have non-default implementations of
equals/hashCode/toString already. Therefore, a default version of these on an interface is always
useless, and it may as well not compile.
For further reading, see this explanation written by Brian Goetz: response to "Allow default
methods to override Object's methods"
Functional interfaces
A core concept introduced in Java 8 is that of a "functional interface". An interface is a functional
interface if it defines exactly one abstract method. For instance, java.lang.Runnable is a functional
interface because it only defines one abstract method:
public abstract void run();
Note that the "abstract" modifier is implied because the method lacks a body. It is not necessary to
specify the "abstract" modifier, as this code does, in order to qualify as a functional interface.
Default methods are not abstract, so a functional interface can define as many default methods as it
likes.
A new annotation, @FunctionalInterface, has been introduced. It can be placed on an interface to
declare the intention of it being a functional interface. It will cause the interface to refuse to
compile unless you've managed to make it a functional interface. It's sort of like @Override in this
way; it declares intention and doesn't allow you to use it incorrectly.
Lambdas
An extremely valuable property of functional interfaces is that they can be instantiated using
lambdas. Here are a few examples of lambdas:
Comma-separated list of inputs with specified types on the left, a block with a return on the right:
(int x, int y) -> { return x + y; }
Comma-separated list of inputs with inferred types on the left, a return value on the right:
(x, y) -> x + y
Single parameter with inferred type on the left, a return value on the right:
x -> x * x
No inputs on left (official name: "burger arrow"), return value on the right:
() -> x
Single parameter with inferred type on the left, a block with no return (void return) on the right:
x -> { System.out.println(x); }
Static method reference:
String::valueOf
Non-static method reference:
Object::toString
Capturing method reference:
x::toString
Constructor reference:
ArrayList::new
You can think of method reference forms as shorthand for the other lambda forms.
Method reference
String::valueOf
x -> String.valueOf(x)
Object::toString
x -> x.toString()
x::toString
() -> x.toString()
ArrayList::new
Of course, methods in Java can be overloaded. Classes can have multiple methods with the same
name but different parameters. The same goes for its constructors. ArrayList::new could refer to
any of its three constructors. The method it resolves to depends on which functional interface it's
being used for.
A lambda is compatible with a given functional interface when their "shapes" match. By "shapes",
I'm referring to the types of the inputs, outputs, and declared checked exceptions.
To give a couple of concrete, valid examples:
Comparator<String> c = (a, b) -> Integer.compare(a.length(),
b.length());
A Comparator<String>'s compare method takes two strings as input, and returns an int. That's
consistent with the lambda on the right, so this assignment is valid.
Runnable r = () -> { System.out.println("Running!"); }
A Runnable's run method takes no arguments and does not have a return value. That's consistent
with the lambda on the right, so this assignment is valid.
The checked exceptions (if present) in the abstract method's signature matter too. The lambda can
only throw a checked exception if the functional interface declares that exception in its signature.
Capturing versus non-capturing lambdas
Lambdas are said to be "capturing" if they access a non-static variable or object that was defined
outside of the lambda body. For example, this lambda captures the variable x:
int x = 5;
return y -> x + y;
In order for this lambda declaration to be valid, the variables it captures must be "effectively final".
So, either they must be marked with the final modifier, or they must not be modified after they're
assigned.
Whether a lambda is capturing or not has implications for performance. A non-capturing lambda is
generally going to be more efficient than a capturing one. Although this is not defined in any
specifications (as far as I know), and you shouldn't count on it for a program's correctness, a noncapturing lambda only needs to be evaluated once. From then on, it will return an identical instance.
Capturing lambdas need to be evaluated every time they're encountered, and currently that performs
much like instantiating a new instance of an anonymous class.
What lambdas don't do
There are a few features that lambdas don't provide, which you should keep in mind. They were
considered for Java 8 but were not included, for simplicity and due to time constraints.
Non-final variable capture - If a variable is assigned a new value, it can't be used within a lambda.
The "final" keyword is not required, but the variable must be "effectively final" (discussed earlier).
This code does not compile:
int count = 0;
List<String> strings = Arrays.asList("a", "b", "c");
strings.forEach(s -> {
count++; // error: can't modify the value of count
});
Exception transparency - If a checked exception may be thrown from inside a lambda, the
functional interface must also declare that checked exception can be thrown. The exception is not
propogated to the containing method. This code does not compile:
void appendAll(Iterable<String> values, Appendable out)
throws IOException { // doesn't help with the error
values.forEach(s -> {
out.append(s); // error: can't throw IOException here
// Consumer.accept(T) doesn't allow it
});
}
There are ways to work around this, where you can define your own functional interface that
extends Consumer and sneaks the IOException through as a RuntimeException. I tried this out in
code and found it to be too confusing to be worthwhile.
Control flow (break, early return) - In the forEach examples above, a traditional continue is
possible by placing a "return;" statement within the lambda. However, there is no way to break out
of the loop or return a value as the result of the containing method from within the lambda. For
example:
final String secret = "foo";
boolean containsSecret(Iterable<String> values) {
values.forEach(s -> {
if (secret.equals(s)) {
??? // want to end the loop and return true, but can't
}
});
}
For further reading about these issues, see this explanation written by Brian Goetz: response to
"Checked exceptions within Block<T>
Why abstract classes can't be instantiated using a lambda
An abstract class, even if it declares only one abstract method, cannot be instantiated with a
lambda.
Two examples of classes with one abstract method are Ordering and CacheLoader from the Guava
library. Wouldn't it be nice to be able to declare instances of them using lambdas like this?
Ordering<String> order = (a, b) -> ...;
CacheLoader<String, String> loader = (key) -> ...;
The most common argument against this was that it would add to the difficulty of reading a lambda.
Instantiating an abstract class in this way could lead to execution of hidden code: that in the
constructor of the abstract class.
Another reason is that it throws out possible optimizations for lambdas. In the future, it may be the
case that lambdas are not evaluated into object instances. Letting users declare abstract classes with
lambdas would prevent optimizations like this.
Besides, there's an easy workaround. Actually, the two example classes from Guava already
demonstrate this workaround. Add factory methods to convert from a lambda to an instance:
Ordering<String> order = Ordering.from((a, b) -> ...);
CacheLoader<String, String> loader =
CacheLoader.from((key) -> ...);
For further reading, see this explanation written by Brian Goetz: response to "Allow lambdas to
implement abstract classes"
java.util.function
Package summary: java.util.function
As demonstrated earlier with Comparator and Runnable, interfaces already defined in the JDK that
happen to be functional interfaces are compatible with lambdas. The same goes for any functional
interfaces defined in your own code or in third party libraries.
But there are certain forms of functional interfaces that are widely, commonly useful, which did not
exist previously in the JDK. A large number of these interfaces have been added to the new
java.util.function package. Here are a few:
Consumer<T> - take a T as input, perform some action and don't return anything
BinaryOperator<T> - take two T's as input, return one T as output, useful for "reduce"
operations
Primitive specializations for most of these exist as well. They're provided in int, long, and double
forms. For instance:
IntConsumer - take an int as input, perform some action and don't return anything
These exist for performance reasons, to avoid boxing and unboxing when the inputs or outputs are
primitives.
java.util.stream
Package summary: java.util.stream
The new java.util.stream package provides utilities "to support functional-style operations on
streams of values" (quoting the javadoc). Probably the most common way to obtain a stream will be
from a collection:
Stream<T> stream = collection.stream();
A stream is something like an iterator. The values "flow past" (analogy to a stream of water) and
then they're gone. A stream can only be traversed once, then it's used up. Streams may also be
infinite.
Streams can be sequential or parallel. They start off as one and may be switched to the other using
stream.sequential() or stream.parallel(). The actions of a sequential stream occur in serial fashion on
one thread. The actions of a parallel stream may be happening all at once on multiple threads.
So, what do you do with a stream? Here is the example given in the package javadocs:
int sumOfWeights = blocks.stream().filter(b -> b.getColor() == RED)
.mapToInt(b -> b.getWeight())
.sum();
Note: The above code makes use of a primitive stream, and a sum() method is only available on
primitive streams. There will be more detail on primitive streams shortly.
A stream provides a fluent API for transforming values and performing some action on the results.
Stream operations are either "intermediate" or "terminal".
Intermediate - An intermediate operation keeps the stream open and allows further
operations to follow. The filter and map methods in the example above are intermediate
operations. The return type of these methods is Stream; they return the current stream to
allow chaining of more operations.
Terminal - A terminal operation must be the final operation invoked on a stream. Once a
terminal operation is invoked, the stream is "consumed" and is no longer usable. The sum
Stateful - A stateful operation imposes some new property on the stream, such as uniqueness
of elements, or a maximum number of elements, or ensuring that the elements are consumed
in sorted fashion. These are typically more expensive than stateless intermediate operations.
Here are short, general descriptions for each Stream method. See the javadocs for more thorough
explanations. Links are provided below for each overloaded form of the operation.
Intermediate operations:
flatMap1234 - Transform each element into zero or more elements by way of another
Stream.
peek1 - Perform some action on each element as it is encountered. Primarily useful for
debugging.
distinct1 - Exclude all duplicate elements according to their .equals behavior. This is a
stateful operation.
sorted12 - Ensure that stream elements in subsequent operations are encountered according
to the order imposed by a Comparator. This is a stateful operation.
limit1 - Ensure that subsequent operations only see up to a maximum number of elements.
This is a stateful, short-circuiting operation.
skip1 - Ensure that subsequent operations do not see the first n elements. This is a stateful
operation.
Terminal operations:
collect12 - Dump the elements in the stream into some container, such as a Collection or
Map.
anyMatch1 - Find out whether at least one of the elements in the stream matches a
allMatch1 - Find out whether every element in the stream matches a Predicate. This is a
short-circuiting operation.
noneMatch1 - Find out whether zero elements in the stream match a Predicate. This is a
short-circuiting operation.
findFirst1 - Find the first element in the stream. This is a short-circuiting operation.
findAny1 - Find any element in the stream, which may be cheaper than findFirst for some
streams. This is a short-circuiting operation.
As noted in the javadocs, intermediate operations are lazy. Only a terminal operation will start the
processing of stream elements. At that point, no matter how many intermediate operations were
included, the elements are then consumed in (usually, but not quite always) a single pass. (Stateful
operations such as sorted() and distinct() may require a second pass over the elements.)
Streams try their best to do as little work as possible. There are micro-optimizations such as eliding
a sorted() operation when it can determine the elements are already in order. In operations that
include limit(x) or substream(x,y), a stream can sometimes avoid performing intermediate map
operations on the elements it knows aren't necessary to determine the result. I'm not going to be
able to do the implementation justice here; it's clever in lots of small but significant ways, and it's
still improving.
Returning to the concept of parallel streams, it's important to note that parallelism is not free. It's
not free from a performance standpoint, and you can't simply swap out a sequential stream for a
parallel one and expect the results to be identical without further thought. There are properties to
consider about your stream, its operations, and the destination for its data before you can (or
should) parallelize a stream. For instance: Does encounter order matter to me? Are my functions
stateless? Is my stream large enough and are my operations complex enough to make parallelism
worthwhile?
There are primitive-specialized versions of Stream for ints, longs, and doubles:
IntStream
LongStream
DoubleStream
One can convert back and forth between an object stream and a primitive stream using the
primitive-specialized map and flatMap functions, among others. To give a few contrived examples:
List<String> strings = Arrays.asList("a", "b", "c");
strings.stream()
// Stream<String>
.mapToInt(String::length) // IntStream
.longs()
// LongStream
.mapToDouble(x -> x / 10.0) // DoubleStream
.boxed()
// Stream<Double>
.mapToLong(x -> 1L)
// LongStream
.mapToObj(x -> "")
// Stream<String>
...
The primitive streams also provide methods for obtaining basic numeric statistics about the stream
as a data structure. You can find the count, sum, min, max, and mean of the elements all from one
terminal operation.
There are not primitive versions for the rest of the primitive types because it would have required
an unacceptable amount of bloat in the JDK. IntStream, LongStream, and DoubleStream were
deemed useful enough to include, and streams of other numeric primitives can represented using
these three via widening primitive conversion.
One of the most confusing, intricate, and useful terminal stream operations is collect. It introduces a
new interface called Collector. This interface is somewhat difficult to understand, but fortunately
there is a Collectors utility class for generating all sorts of useful Collectors. For example:
List<String> strings = values.stream()
.filter(...)
.map(...)
.collect(Collectors.toList());
If you want to put your stream elements into a Collection, Map, or String, then Collectors probably
has what you need. It's definitely worthwhile to browse through the javadoc of that class.
Generic type inference improvements
Date.toInstant()
Date.from(Instant)
Calendar.toInstant--
The new API prefers enums over integer constants for things like months and days of the week.
So, what's in it? The package-level javadocs do an excellent job of explaining the additional types.
I'll give a brief rundown of some noteworthy parts.
Extremely useful value types:
Year, Month, YearMonth, MonthDay, DayOfWeek (ok, maybe Year is a little less useful
than the others)
Duration, Period - although if you're trying to find the amount of time between two dates,
you might be looking for ChronoUnit instead (see below)
ChronoUnit - for figuring out the amount of time bewteen two points, e.g.
ChronoUnit.DAYS.between(t1, t2)
The new value types are, for the most part, supported by JDBC. There are minor exceptions, such
as ZonedDateTime which has no counterpart in SQL.
Collections API additions
The fact that interfaces can define default methods allowed the JDK authors to make a large
number of additions to the collection API interfaces. Default implementations for these are
provided on all the core interfaces, and more efficient or well-behaved overridden implementations
were added to all the concrete classes, where applicable.
Here's a list of the new methods:
Iterable.forEach(Consumer)
Iterator.forEachRemaining(Consumer)
Collection.removeIf(Predicate)
Collection.spliterator()
Collection.stream()
Collection.parallelStream()
List.sort(Comparator)
List.replaceAll(UnaryOperator)
Map.forEach(BiConsumer)
Map.replaceAll(BiFunction)
Map.putIfAbsent(K, V)
Map.remove(Object, Object)
Map.replace(K, V, V)
Map.replace(K, V)
Map.computeIfAbsent(K, Function)
Map.computeIfPresent(K, BiFunction)
Map.compute(K, BiFunction)
Map.merge(K, V, BiFunction)
Map.getOrDefault(Object, V)
Also, Iterator.remove() now has a default, throwing implementation, which makes it slightly easier
to define unmodifiable iterators.
Collection.stream() and Collection.parallelStream() are the main gateways into the stream API.
There are other ways to generate streams, but those are going to be the most common by far.
The addition of List.sort(Comparator) is fantastic. Previously, the way to sort an ArrayList was this:
Collections.sort(list, comparator);
That code, which was your only option in Java 7, was frustratingly inefficient. It would dump the
list into an array, sort the array, then use a ListIterator to insert the array contents into the list in new
positions.
The default implementation of List.sort(Comparator) still does this, but concrete implementing
classes are free to optimize. For instance, ArrayList.sort invokes Arrays.sort on the ArrayList's
internal array. CopyOnWriteArrayList does the same.
Performance isn't the only potential gain from these new methods. They can have more desirable
semantics, too. For instance, sorting a Collections.synchronizedList() is an atomic operation using
list.sort. You can iterate over all its elements as an atomic operation using list.forEach. Previously
ForkJoinPool.commonPool()
ConcurrentHashMap(v8)
The following come with forms for parallel, sequentially, Object, int, long, and double.
There are too many of these to link, so see the ConcurrentHashMap javadocs for more
information.
o ConcurrentHashMap.reduce...
o ConcurrentHashMap.search...
o ConcurrentHashMap.forEach...
ConcurrentHashMap.newKeySet()
ConcurrentHashMap.newKeySet(int)
CompletableFuture
CompletionStage
StampedLock
LongAdder
LongAccumulator
DoubleAdder
DoubleAccumulator
CountedCompleter
Executors.newWorkStealingPool()
Executors.newWorkStealingPool(int)
The following come with forms for AtomicReference, AtomicInteger, AtomicLong, and the
atomic array versions for each.
o AtomicReference.getAndUpdate(UnaryOperator)
o AtomicReference.updateAndGet(UnaryOperator)
o AtomicReference.getAndAccumulate(V, UnaryOperator)
o AtomicReference.accumulateAndGet(V, UnaryOperator)
BufferedReader.lines()
Files.list(Path)
Files.walk(Path, FileVisitOption...)
Files.lines(Path, Charset)
DirectoryStream.stream()
unchecked exception.
Most of these additions give you ways to obtain java.util.stream.Stream from files and
InputStreams. They're a bit different from the streams you obtain from regular collections though.
For one, they may throw UncheckedIOException. Also, they are instances of streams where using
the stream.close() method is necessary. Streams implement AutoCloseable and can therefore be
used in try-with-resources statements. Streams also have an onClose(Runnable) intermediate
operation that I didn't list in the earlier section about streams. It allows you to attach handlers to a
stream that execute when it is closed. Here is an example:
// Print the lines in a file, then "done"
try (Stream lines = Files.lines(path, UTF_8)) {
lines.onClose(() -> System.out.println("done"))
.forEach(System.out::println);
}
Reflection and annotation changes
AnnotatedArrayType
AnnotatedElement.getAnnotationsByType(Class)
AnnotatedElement.getDeclaredAnnotation(Class)
AnnotatedElement.getDeclaredAnnotationsByType(Class)
AnnotatedParameterizedType
AnnotatedType
AnnotatedTypeVariable
AnnotatedWildcardType
Class.getAnnotatedInterfaces()
Class.getAnnotatedSuperclass()
Class.getTypeName()
Class.toGenericString()
Executable
Field.getAnnotatedType()
Method.isDefault()
Modifier.parameterModifiers()
Native
Repeatable
Type.getTypeName()
TypeVariable.getAnnotatedBounds()
Annotations are allowed in more places, e.g. List<@Nullable String>. The biggest impact of this is
likely to be for static analysis tools such as Sonar and FindBugs.
This JSR 308 website does a better job of explaining the motivation for these changes than I could
possibly do: "Type Annotations (JSR 308) and the Checker Framework"
Nashorn JavaScript Engine
Summary of proposal: JEP 174: Nashorn JavaScript Engine
I did not experiment with Nashorn so I know very little beyond what's described in the proposal
above. Short version: It's the successor to Rhino. Rhino is old and a little bit slow, and the
developers decided they'd be better off starting from scratch.
Other miscellaneous additions to java.lang, java.util, and elsewhere
ThreadLocal.withInitial(Supplier)
String.join(CharSequence, Charsequence...)
String.join(CharSequence, Iterable)
Collections.unmodifiableNavigableSet(NavigableSet)
Collections.unmodifiableNavigableMap(NavigableMap)
Collections.synchronizedNavigableSet(NavigableSet)
Collections.synchronizedNavigableMap(NavigableMap)
Collections.checkedQueue(Queue, Class)
Collections.checkedNavigableSet(NavigableSet, Class)
Collections.emptySortedSet()
Collections.emptyNavigableSet()
Collections.emptySortedMap()
Collections.emptyNavigableMap()
SplittableRandom
Optional
OptionalInt
OptionalLong
OptionalDouble
Base64
StringJoiner
Spliterator
Spliterators
Comparator.naturalOrder()
Comparator.reverseOrder()
Comparator.nullsFirst(Comparator)
Comparator.nullsLast(Comparator)
Comparator.comparing(Function, Comparator)
Comparator.comparing(Function)
Comparator.comparingInt(ToIntFunction)
Comparator.comparingLong(ToLongFunction)
Comparator.comparingDouble(ToDoubleFunction)
Comparator.reversed()
Comparator.thenComparing(Comparator)
Comparator.thenComparing(Function, Comparator)
Comparator.thenComparing(Function)
Comparator.thenComparingInt(ToIntFunction)
Comparator.thenComparingLong(ToLongFunction)
Comparator.thenComparingDouble(ToDoubleFunction)
The following come in forms for array = T[], int[], long[], double[]. There are too many of
these to link, so see the Arrays javadocs for more information.
o Arrays.spliterator(array)
o Arrays.spliterator(array, int, int)
o Arrays.stream(array)
o Arrays.stream(array, int, int);
o Arrays.setAll(array, IntFunction)
o Arrays.parallelSetAll(array, IntFunction)
o Arrays.parallelPrefix(array, BinaryOperator)
o Arrays.parallelPrefix(array, int, int, BinaryOperator)
Math.addExact(int, int)
Math.addExact(long, long)
Math.subtractExact(int, int)
Math.subtractExact(long, long)
Math.multiplyExact(int, int)
Math.multiplyExact(long, long)
Math.negateExact(int)
Math.negateExact(long)
Math.decrementExact(int)
Math.decrementExact(long)
Math.incrementExact(int)
Math.incrementExact(long)
Math.floorDiv(int, int)
Math.floorDiv(long, long)
Math.floorMod(int, int)
Math.floorMod(long, long)
Math.toIntExact(long)
Math.nextDown(float)
Math.nextDown(double)
Integer.min(int, int)
Integer.max(int, int)
Integer.sum(int, int)
Long.min(long, long)
Long.max(long, long)
Long.sum(long, long)
Double.min(double, double)
Double.max(double, double)
Double.sum(double, double)
Boolean.logicalAnd(boolean, boolean)
Boolean.logicalOr(boolean, boolean)
Boolean.logicalXor(boolean, boolean)
Integer.toUnsignedLong(int)
Integer.toUnsignedString(int)
Integer.toUnsignedString(int, int)
Integer.parseUnsignedInt(String)
Integer.parseUnsignedInt(String, int)
Integer.compareUnsigned(int, int)
Long.toUnsignedString(long, int)
Long.toUnsignedString(long)
Long.parseUnsignedLong(String, int)
Long.parseUnsignedLong(String)
Long.compareUnsigned(long, long)
Long.divideUnsigned(long, long)
Long.remainderUnsigned(long, long)
BigInteger.longValueExact()
BigInteger.intValueExact()
BigInteger.shortValueExact()
BigInteger.byteValueExact()
Objects.requireNonNull(T, Supplier)
Random.ints()
Random.longs()
Random.doubles()
SecureRandom.getStrongSecureRandom()
BitSet.stream()
IntSummaryStatistics
LongSummaryStatistics
DoubleSummaryStatistics
Pattern.splitAsStream(CharSequence)
Pattern.asPredicate()
Process.waitFor(long, TimeUnit)
Process.destroyForcibly()
Process.isAlive()
ZipFile.stream()
Adler32.update(ByteBuffer)
CRC32.update(ByteBuffer)
There is too much there to talk about, but I'll pick out a few noteworthy items.
ThreadLocal.withInitial(Supplier<T>) makes declaring thread-local variables with initial values
much nicer. Previously you would supply an initial value like this:
ThreadLocal<List<String>> strings =
new ThreadLocal<List<String>>() {
@Override
protected List<String> initialValue() {
return new ArrayList<>();
}
};
Now it's like this:
ThreadLocal<List<String>> strings =
ThreadLocal.withInital(ArrayList::new);
Optional<T> appears in the stream API as the return value for methods like min/max,
findFirst/Any, and some forms of reduce. It's used because there might not be any elements in the
stream, and it provides a fluent API for handling the "some result" versus "no result" cases. You can
provide a default value, throw an exception, or execute some action only if the result exists.
It's very, very similar to Guava's Optional class. It's nothing at all like Option in Scala, nor is it
trying to be, and the name similarity there is purely coincidental.
Aside: it's interesting that Java 8's Optional and Guava's Optional ended up being so similar, despite
the absurd amount of debate that occurred over its addition to both libraries.
"FYI.... Optional was the cause of possibly the single greatest conflagration on the internal Java
libraries discussion lists ever."
Kevin Bourrillion in response to "Some new Guava classes targeted for release 10"
"On a purely practical note, the discussions surrounding Optional have exceeded its design budget