Skip to content

Bugfix Release 2.0.6

Compare
Choose a tag to compare
@danieldietrich danieldietrich released this 12 May 08:38
· 1479 commits to master since this release

Committers

Daniel Dietrich
Gregor Trefs
Jan Ferko
Pap Lőrinc
Przemek Wesołek
Ruslan Sennov

Changelog

Base

#1891 Changed generic bounds of Predicates allOf(), anyOf() and noneOf()

We needed to change the signatures because some Match expressions did not compile.

-   public static <T> Predicate<T> allOf(Predicate<? super T>... predicates) { ... }
+   public static <T> Predicate<T> allOf(Predicate<T>... predicates) { ... }

Instead of

Match(value).of(
        Case(instanceOf(Either.LeftProjection.class), false),
        Case(instanceOf(Either.RightProjection.class), false),
        Case(instanceOf(Future.class), false),
        Case(instanceOf(Iterator.class), false),
        Case(instanceOf(Validation.class), false),
        Case(instanceOf(Either.class), true),
        Case(instanceOf(Option.class), true),
        Case(instanceOf(Try.class), true),
        Case(instanceOf(Traversable.class), true)
);

we are now able to write

Match(value).of(
        Case(anyOf(
                instanceOf(Either.LeftProjection.class),
                instanceOf(Either.RightProjection.class),
                instanceOf(Future.class),
                instanceOf(Iterator.class),
                instanceOf(Validation.class)
        ), false),
        Case(anyOf(
                instanceOf(Either.class),
                instanceOf(Option.class),
                instanceOf(Try.class),
                instanceOf(Traversable.class)
        ), true)
);

The second variant did not compile before.

#1892 Make interfaces Serializable

We (unintentionally) changed the interface serializability. E.g. Option wasn't serializable any more (but the classes Some and None are). This hindered us from adding fields of interface types (like Option) to serializable classes.

Now interfaces extend Serializable. We made an exception for stateful types like Future, Promise and Iterator. These interfaces still to not extend Serializable.

Collections

#1796 Fixed stackoverflow problem of List.hashCode().

The hashCode() calculation for big lists now works as expected.

List.range(0, 1_000_000)).hashCode();

Note: Additionally we changed the hashCode() of the empty HashArrayMappedTrie, which is the basis for HashMap and HashSet.

#1792 Fixes deadlock on concurrent class initialization

We could provoke a deadlock when concurrently using Iterators and the Iterator class wasn't yet initialized by the underlying ClassLoader:

import javaslang.collection.AbstractIterator;
import javaslang.collection.Iterator;

/**
 * Created by aeremeev on 25.12.16.
 */
public class DeadlockTest {

    public static void main(String[] args) {
        new Thread(Iterator::empty).start();
        new A();
        System.out.println("Success");
    }

    private static class A extends AbstractIterator<Void> {
        @Override
        protected Void getNext() {
            return null;
        }

        @Override
        public boolean hasNext() {
            return false;
        }
    }
}

#1816, #1823 HashArrayMappedTrie.equals() and LeafList.hashCode() fixed

The following maps should be equal because there is no underlying order.

final Map<String, Integer> map1 = HashMap.of("Aa", 1, "BB", 2);
final Map<String, Integer> map2 = HashMap.of("BB", 2, "Aa", 1);
assertThat(map1).isEqualTo(map2);

Please note that we provoked a hash collision in the example above:

jshell> "Aa".hashCode()
$1 ==> 2112

jshell> "BB".hashCode()
$2 ==> 2112

#1854 LinkedHashMap.keySet() order

The key set of a LinkedHashMap needs to be a linked set.

final Set<Integer> keySet = LinkedHashMap.of(4, "d", 1, "a", 2, "b").keySet();
assertThat(keySet.mkString()).isEqualTo("412");

#1858 Set.add(existing) does nothing, Map.put(existing, any) overwrites

The Javaslang implementation now acts like this:

Set ovewrites equal elements
BitSet no
HashSet no
LinkedHashSet no
TreeSet no
Map ovewrites (k,v) when
key equal, value unequal
ovewrites (k,v) when
key equal, value equal
overwrites old key
with new key
HashMap yes yes yes
LinkedHashMap yes (appends) yes (appends) yes
TreeMap yes yes yes

There is one anomaly in Scala: Despite all other Map implementations, HashMap does not update (key, value) when putting a (key, value) that is already present.

Especially this may bite the user when working with the Map interface and it is not clear which implementations is currently used (see Liskov Substitution Principle).

In Javaslang, we do consistent updates along all Map implementations.

#1878 Fixes Traversable.containsAll(Iterable)

I can't think what came over me.

default boolean containsAll(Iterable<? extends T> elements) {
-   final HashSet<T> uniqueElements = HashSet.ofAll(elements);
-   return toSet().intersect(uniqueElements).size() == uniqueElements.size();
+   for (T element : elements) {
+       if (!contains(element)) {
+           return false;
+       }
+   }
+   return true;
}

#1907 Fixes memory leak of grouped iterator

Infinite iterators can be grouped now in constant memory:

Iterator.continually(1)
        .grouped(100)
        .zipWithIndex()
        .forEach(t -> System.out.println("Group " + t._2));

Functions

#1837 Changed memoization's computeIfAbsent to synchronized get and put

We now support function value memoization in recursive calls:

private static final Function2<Integer, Integer, Integer> recurrent1 = (i1, i2) -> i1 <= 0 ? i1 : Function2Test.recurrent2.apply(i1 - 1, i2) + 1;
private static final Function2<Integer, Integer, Integer> recurrent2 = Function2Test.recurrent1.memoized();

@Test
public void shouldCalculatedRecursively() {
    assertThat(recurrent1.apply(11, 11)).isEqualTo(11);
    assertThat(recurrent1.apply(22, 22)).isEqualTo(22);
}

Property Testing

#1700 Optimized random value generators

We now internally use Iterator instead of Stream:

static <T> Gen<T> of(T seed, Function<? super T, ? extends T> next) {
    Objects.requireNonNull(next, "next is null");
-   final Iterator<T> iterator = Stream.iterate(seed, next).iterator();
+   final Iterator<T> iterator = Iterator.iterate(seed, next);
    return ignored -> iterator.next();
}

Additionally we rewrote

  • frequency(Iterable<Tuple2<Integer, Gen<T>>> generators)
  • intersperse(Gen<T> other)

Info: This change contains a backward compatible API change:

interface Gen<T> {
-   static Gen<Character> choose(char[] characters) {
+   static Gen<Character> choose(char... characters) {
}

#1702 ArrayStoreException in Gen.choose(Iterable)

For instances of generic interfaces, choose was throwing an ArrayStoreException because of different runtime types of the elements within the iterable.

static <T> Gen<T> choose(Iterable<T> values) {
    ...
-   final T[] array = stream.toJavaArray((Class<T>) stream.head().getClass());
+   final T[] array = (T[]) iterator.toJavaArray();
    ...
}

#1759 Gen.frequency filters non-positive frequencies

Now Gen.frequency(Iterable) throws an IllegalArgumentException if there are non-positive frequencies (including zero).

Javadoc

#1735 Correct prefix-delimiter-suffix in mkString() javadoc

    /**
     ...
-    * This has the same effect as calling {@code mkString(delimiter, "", "")}.
+    * This has the same effect as calling {@code mkString("", delimiter, "")}.
     ...
     */