I am trying to create a two dimensional list in java.
My first and preferred method is as so:
List<List<Integer>> seqList = IntStream.range(0, n)
.mapToObj(ArrayList<Integer>::new)
.collect(Collectors.toList());
However, for some reason this method takes too long and I get a timeout.
On the other hand, when I tried to create the two dimensional list using java 7 like so, there was no timeout.
List<List<Integer>> seqList = new ArrayList<>();
for(int i = 0; i < n; i++) {
seqList.add(new ArrayList<>());
}
I am trying to use as much java-8 streams as possible. Could someone explain to me why my java-8 code is taking too long and what I can do to make it run in the same time complexity as the java-7 code.
This is an alternative way to do it.
int n = 10;
List<List<Integer>> seqList =Stream.<List<Integer>>generate(()->new ArrayList<>())
.limit(n).collect(Collectors.toList());
Thanks to Jacob G I was able to see the problem.
The call .mapToObj(ArrayList<Integer>::new) was creating ArrayLists of varying size. It was equivalent to .mapToObj(i -> new ArrayList<Integer>(i)). Now this means that creating new arraylist objects when i is huge take longer hence the timeout. The better code is as follows:
List<List<Integer>> seqList2 = IntStream.range(0, n)
.mapToObj(i -> new ArrayList<Integer>())
.collect(Collectors.toList());
The relative cost of the streaming APIs will be high, even with the correction. This can be seen by walking through the many steps which are performed. The complexity is rather quite extraordinary.
The code examples, below, are from the IBM Java SE Runtime Environment version 1.8.
// A walkthrough of the operation:
//
// "Create a list of lists by collecting the results of applying the ArrayList
// initializer to the stream of 'int' values ranging from 0 to 10."
static {
List<List<Integer>> seqList = IntStream.range(0, 10)
.mapToObj( ArrayList<Integer>::new )
.collect( Collectors.toList() );
}
// First step: Create an 'int' Stream.
//
// Roughly, create an 'int' iterator, then wrap that in a 'int' stream.
//
// The iterator is responsible for knowing the initial and final values
// over the range of iteration, and for providing basic iteration.
//
// However, 'mapToObj' is part of the streaming API. The iterator
// must be put into a stream to access that API.
// The 'int' stream factory method.
//
// Fan out to 'RangeIntSpliterator.init' and to 'StreamSupport.intStream'.
//
// The 'int' stream is created with 'parallel' set to false.
class IntStream {
public static IntStream range(int startInclusive, int endExclusive) {
if ( startInclusive >= endExclusive ) {
return empty();
} else {
return StreamSupport.intStream(
new Streams.RangeIntSpliterator(startInclusive, endExclusive, false),
false );
}
}
}
// The 'int' iterator type.
//
// After setup, 'forEachRemaining' will be used to perform
// the 'int' iteration.
class RangeIntSpliterator implements Spliterator.OfInt {
protected int from;
protected final int upTo;
protected int last;
RangeIntSpliterator(int from, int upTo, boolean closed) {
this( from, upTo, (closed ? 1 : 0) );
}
void forEachRemaining(Consumer<? super Integer> action);
void forEachRemaining(IntConsumer consumer);
}
// The 'int' stream factory method.
//
// Fan out to 'IntPipeline.Head<>.init'. 'IntPipeline.Head' extends
// 'IntPipeline', which extends 'AbstractPipeline'.
//
// 'IntPipeline.mapToObj' creates an stream of 'ArrayList' instances
// out of the stream of 'int' instances.
class StreamSupport {
public static IntStream intStream(Spliterator.OfInt spliterator, boolean parallel) {
return new IntPipeline.Head<>(
spliterator,
StreamOpFlag.fromCharacteristics(spliterator),
parallel );
}
}
class IntPipeLine.Head<> extends IntPipeline<> {
Head(Spliterator<Integer> source, int sourceFlags, boolean parallel) {
super(source, sourceFlags, parallel);
}
}
class IntPipeline<>
extends AbstractPipeline<, Integer, IntStream>
implements IntStream {
IntPipeline(Spliterator<Integer> source, int sourceFlags, boolean parallel) {
super(source, sourceFlags, parallel);
}
<U> Stream<U> mapToObj(IntFunction<? extends U> mapper);
}
class AbstractPipeline {
AbstractPipeline(Spliterator<?> source, int sourceFlags, boolean parallel) {
this.previousStage = null;
this.sourceSpliterator = source;
this.sourceStage = this;
this.sourceOrOpFlags = ( sourceFlags & StreamOpFlag.STREAM_MASK );
this.combinedFlags = ( (~(sourceOrOpFlags << 1)) & StreamOpFlag.INITIAL_OPS_VALUE );
this.depth = 0;
this.parallel = parallel;
}
}
// Second step: Create a second stream by composing the 'int' stream with the ArrayList
// initializer.
//
// Fan out to 'ReferencePipeline.StatelessOp'. 'StatelessOp' extends 'ReferencePipeline',
// which extends 'AbstractPipeline'.
class IntPipeline {
#Override
public final <U> Stream<U> mapToObj(IntFunction<? extends U> mapper) {
Objects.requireNonNull(mapper);
return new ReferencePipeline.StatelessOp<Integer, U>(
this,
StreamShape.INT_VALUE,
(StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) ) {
Sink<Integer> opWrapSink(int flags, Sink<U> sink) {
return new Sink.ChainedInt<U>(sink) {
public void accept(int t) {
downstream.accept( mapper.apply(t) );
}
};
}
};
}
}
class StatelessOp<E_IN, E_OUT> extends ReferencePipeline<E_IN, E_OUT> {
StatelessOp(AbstractPipeline<?, E_IN, ?> upstream, StreamShape inputShape, int opFlags) {
super(upstream, opFlags);
assert upstream.getOutputShape() == inputShape;
}
abstract class ReferencePipeline<P_IN, P_OUT>
extends AbstractPipeline<P_IN, P_OUT, Stream<P_OUT>>
implements Stream<P_OUT> {
ReferencePipeline(Supplier<? extends Spliterator<?>> source, int sourceFlags) {
super(source, sourceFlags);
}
}
abstract class AbstractPipeline<E_IN, E_OUT, S extends BaseStream<E_OUT, S>>
extends PipelineHelper<E_OUT> implements BaseStream<E_OUT, S> {
AbstractPipeline(AbstractPipeline<?, E_IN, ?> previousStage, int opFlags) {
if ( previousStage.linkedOrConsumed ) {
throw new IllegalStateException(MSG_STREAM_LINKED);
}
previousStage.linkedOrConsumed = true;
previousStage.nextStage = this;
this.previousStage = previousStage;
this.sourceOrOpFlags = opFlags & StreamOpFlag.OP_MASK;
this.combinedFlags = StreamOpFlag.combineOpFlags(opFlags, previousStage.combinedFlags);
this.sourceStage = previousStage.sourceStage;
if ( opIsStateful() ) {
sourceStage.sourceAnyStateful = true;
}
this.depth = previousStage.depth + 1;
}
}
// Third step: Obtain the collector which is to be used by the 'int' stream.
//
// Note use of 'CH_ID', which marks the collector as an 'identity finisher'.
class Collectors {
static final Set<Collector.Characteristics> CH_ID =
Collections.unmodifiableSet( EnumSet.of(Collector.Characteristics.IDENTITY_FINISH) );
public static <T> Collector<T, ?, List<T>> toList() {
return new CollectorImpl<>(
(Supplier<List<T>>) ArrayList::new,
List::add,
(left, right) -> { left.addAll(right); return left; },
CH_ID);
}
}
class CollectorImpl<T, A, R> implements Collector<T, A, R> {
private final Supplier<A> supplier;
private final BiConsumer<A, T> accumulator;
private final BinaryOperator<A> combiner;
private final Function<A, R> finisher;
private final Set<Characteristics> characteristics;
CollectorImpl(
Supplier<A> supplier,
BiConsumer<A, T> accumulator,
BinaryOperator<A> combiner,
Function<A,R> finisher,
Set<Characteristics> characteristics) {
this.supplier = supplier;
this.accumulator = accumulator;
this.combiner = combiner;
this.finisher = finisher;
this.characteristics = characteristics;
}
CollectorImpl(
Supplier<A> supplier,
BiConsumer<A, T> accumulator,
BinaryOperator<A> combiner,
Set<Characteristics> characteristics) {
this(supplier, accumulator, combiner, castingIdentity(), characteristics);
}
}
// Fourth step: Start collection.
//
// Push the collector through 'ReduceOps.makeRef'.
class ReferencePipeline {
public final <R, A> R collect(Collector<? super P_OUT, A, R> collector) {
A container;
if ( isParallel() &&
(collector.characteristics().contains(Collector.Characteristics.CONCURRENT)) &&
(!isOrdered() ||
collector.characteristics().contains(Collector.Characteristics.UNORDERED))) {
container = collector.supplier().get();
BiConsumer<A, ? super P_OUT> accumulator = collector.accumulator();
forEach(u -> accumulator.accept(container, u));
} else {
container = evaluate( ReduceOps.makeRef(collector) );
}
return collector.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)
? (R) container
: collector.finisher().apply(container);
}
}
class ReduceOps {
public static <T, I> TerminalOp<T, I> makeRef(Collector<? super T, I, ?> collector) {
Supplier<I> supplier = Objects.requireNonNull(collector).supplier();
BiConsumer<I, ? super T> accumulator = collector.accumulator();
BinaryOperator<I> combiner = collector.combiner();
class ReducingSink extends Box<I> implements AccumulatingSink<T, I, ReducingSink> {
public void begin(long size) {
state = supplier.get();
}
public void accept(T t) {
accumulator.accept(state, t);
}
public void combine(ReducingSink other) {
state = combiner.apply(state, other.state);
}
}
return new ReduceOp<T, I, ReducingSink>(StreamShape.REFERENCE) {
public ReducingSink makeSink() {
return new ReducingSink();
}
};
}
}
class ReduceOp<T, R, S extends AccumulatingSink<T, R, S>> implements TerminalOp<T, R> {
private final StreamShape inputShape;
ReduceOp(StreamShape shape) {
inputShape = shape;
}
}
// Fifth step: Walk into the stream API.
class ReferencePipeline {
<R> R evaluate(TerminalOp<E_OUT, R> terminalOp) {
assert ( getOutputShape() == terminalOp.inputShape() );
if ( linkedOrConsumed ) {
throw new IllegalStateException(MSG_STREAM_LINKED);
}
linkedOrConsumed = true;
return ( isParallel()
? terminalOp.evaluateParallel( this, sourceSpliterator( terminalOp.getOpFlags() ) )
: terminalOp.evaluateSequential( this, sourceSpliterator( terminalOp.getOpFlags() ) ) );
}
}
class AbstractPipeline {
Spliterator<E_OUT> sourceStageSpliterator() {
if ( this != sourceStage ) {
throw new IllegalStateException();
}
if ( linkedOrConsumed ) {
throw new IllegalStateException(MSG_STREAM_LINKED);
}
linkedOrConsumed = true;
if ( sourceStage.sourceSpliterator != null ) {
Spliterator<E_OUT> s = sourceStage.sourceSpliterator;
sourceStage.sourceSpliterator = null;
return s;
} else if ( sourceStage.sourceSupplier != null ) {
Spliterator<E_OUT> s = (Spliterator<E_OUT>) sourceStage.sourceSupplier.get();
sourceStage.sourceSupplier = null;
return s;
} else {
throw new IllegalStateException(MSG_CONSUMED);
}
}
}
class ReduceOp {
public <P_IN> R evaluateSequential(
PipelineHelper<T> helper,
Spliterator<P_IN> spliterator) {
return helper.wrapAndCopyInto( makeSink(), spliterator ).get();
}
}
class AbstractPipeline {
final <P_IN, S extends Sink<E_OUT>> S wrapAndCopyInto(S sink, Spliterator<P_IN> spliterator) {
copyInto( wrapSink( Objects.requireNonNull(sink) ), spliterator );
return sink;
}
}
<P_IN> Sink<P_IN> wrapSink(Sink<E_OUT> sink) {
Objects.requireNonNull(sink);
for ( AbstractPipeline p = AbstractPipeline.this; p.depth > 0; p = p.previousStage ) {
sink = p.opWrapSink( p.previousStage.combinedFlags, sink );
}
return (Sink<P_IN>) sink;
}
class StatelessOp {
Sink<Integer> opWrapSink(int flags, Sink<U> sink) {
return new Sink.ChainedInt<U>(sink) {
public void accept(int t) {
downstream.accept( mapper.apply(t) );
}
};
}
}
// Sixth step: Perform the actual iteration and collection.
//
// Ignoring 'begin' and 'end', iteration and collection occurs in the call
// to 'forEachRemaining'.
class AbstractPipeline {
<P_IN> void copyInto(Sink<P_IN> wrappedSink, Spliterator<P_IN> spliterator) {
Objects.requireNonNull(wrappedSink);
if ( !StreamOpFlag.SHORT_CIRCUIT.isKnown( getStreamAndOpFlags() ) ) {
wrappedSink.begin( spliterator.getExactSizeIfKnown() );
spliterator.forEachRemaining(wrappedSink);
wrappedSink.end();
} else {
copyIntoWithCancel(wrappedSink, spliterator);
}
}
}
class RangeIntSpliterator implements Spliterator.OfInt {
void forEachRemaining(Consumer<? super Integer> action) {
if ( action instanceof IntConsumer ) {
forEachRemaining((IntConsumer) action);
} else {
if ( Tripwire.ENABLED ) {
Tripwire.trip(getClass(), "{0} calling Spliterator.OfInt.forEachRemaining((IntConsumer) action::accept)");
forEachRemaining((IntConsumer) action::accept);
}
}
}
void forEachRemaining(IntConsumer consumer) {
Objects.requireNonNull(consumer);
int i = from;
final int hUpTo = upTo;
int hLast = last;
from = upTo;
last = 0;
while ( i < hUpTo ) {
consumer.accept(i++);
}
if ( hLast > 0 ) {
consumer.accept(i);
}
}
}
// Seventh step: For each iteration, unwind and perform the mapping and
// collection operations.
class new Sink.ChainedInt<U>(sink) {
public void accept(int t) {
downstream.accept( mapper.apply(t) );
}
}
class ArrayList {
public ArrayList(int initialCapacity) {
// ...
}
}
class ReducingSink {
public void accept(T t) {
accumulator.accept(state, t);
}
}
class ArrayList {
public boolean add(E e) {
// ...
}
}
// Eigth step: Walking out with the return value.
IntPipeline$4(AbstractPipeline<E_IN,E_OUT,S>).wrapAndCopyInto(S, Spliterator<P_IN>)
-- returns a 'ReducingSink' instance.
ReduceOps$3(ReduceOps$ReduceOp<T,R,S>).evaluateSequential(PipelineHelper<T>, Spliterator<P_IN>)
-- returns the 'ArrayList' instance.
IntPipeline$4(AbstractPipeline<E_IN,E_OUT,S>).evaluate(TerminalOp<E_OUT,R>)
-- returns the 'ArrayList' instance.
IntPipeline$4(ReferencePipeline<P_IN,P_OUT>).collect(Collector<? super P_OUT,A,R>)
-- returns the 'ArrayList' instance.
Tester.main
Related
public interface IPage<T> extends Serializable {
/** #deprecated */
#Deprecated
default String[] descs() {
return null;
}
/** #deprecated */
#Deprecated
default String[] ascs() {
return null;
}
List<OrderItem> orders();
default Map<Object, Object> condition() {
return null;
}
default boolean optimizeCountSql() {
return true;
}
default boolean isSearchCount() {
return true;
}
default long offset() {
return this.getCurrent() > 0L ? (this.getCurrent() - 1L) * this.getSize() : 0L;
}
default long getPages() {
if (this.getSize() == 0L) {
return 0L;
} else {
long pages = this.getTotal() / this.getSize();
if (this.getTotal() % this.getSize() != 0L) {
++pages;
}
return pages;
}
}
default IPage<T> setPages(long pages) {
return this;
}
default void hitCount(boolean hit) {
}
default boolean isHitCount() {
return false;
}
List<T> getRecords();
IPage<T> setRecords(List<T> records);
long getTotal();
IPage<T> setTotal(long total);
long getSize();
IPage<T> setSize(long size);
long getCurrent();
IPage<T> setCurrent(long current);
default <R> IPage<R> convert(Function<? super T, ? extends R> mapper) {
List<R> collect = (List)this.getRecords().stream().map(mapper).collect(Collectors.toList());
return this.setRecords(collect);
}
default String cacheKey() {
StringBuilder key = new StringBuilder();
key.append(this.offset()).append(":").append(this.getSize());
List<OrderItem> orders = this.orders();
if (CollectionUtils.isNotEmpty(orders)) {
Iterator var3 = orders.iterator();
while(var3.hasNext()) {
OrderItem item = (OrderItem)var3.next();
key.append(":").append(item.getColumn()).append(":").append(item.isAsc());
}
}
return key.toString();
}
}
This is the source code of a framework,
when I use convert() function
default <R> IPage<R> convert(Function<? super T, ? extends R> mapper) {
List<R> collect = (List)this.getRecords().stream().map(mapper).collect(Collectors.toList());
return this.setRecords(collect);
}
what makes me wonder is that The return type is new type variable R
and he just call the this.setRecords(collect);
but setRecords() funcation just receive List <T>!
IPage<T> setRecords(List<T> records);
To verify this, I wrote an interface myself, but the compilation failed
public interface IPage<T> {
IPage<T> setRecords(List<T> list);
default <R> IPage<R> convert() {
List<R> collect = new ArrayList<>();
return this.setRecords(collect); //error
}
}
Can someone help me solve my doubts?Thank you very much!
The source code of my-batis plus contains an additional cast to IPage to change the type of this.
The method code is:
default <R> IPage<R> convert(Function<? super T, ? extends R> mapper) {
List<R> collect = this.getRecords().stream().map(mapper).collect(toList());
return ((IPage<R>) this).setRecords(collect);
}
I have 2 different objects, Parameter and Variable which are fields of Context.
I have also third object: ParameterBase which is not part of Context.
All 3 classes are auto-generated, I can't modify them.
All 3 classes have 2 fields: name and value which are of type String.
All 3 classes have getters and setters.
I implemented the following methods:
public static List<ParametersBase> removeDuplicate(List<ParametersBase> parameterList) {
return new ArrayList<>(parameterList.stream().collect(
Collectors.toMap(
ParametersBase::getParamName,
Function.identity(),
Utils::mergeDuplicate
)
).values());
}
private static ParametersBase mergeDuplicate(ParametersBase a, ParametersBase b) {
if (a.getParamValue().equals(b.getParamValue())) {
return a;
} else {
throw new IllegalArgumentException("Error message");
}
}
I want to do use this methods for the other 2 classes, so I did that:
private static void removeDuplicatesParams(Context context) {
final List<ParametersBase> parameterList = emptyIfNull(
context.getParameters()).stream()
.map(parameter -> new ParametersBase()
.paramName(parameter.getParamName())
.paramValue(parameter.getParamValue()))
.collect(Collectors.toList());
List<ParametersBase> distinctParameterList = removeDuplicate(parameterList);
List<Parameter> distinctParametersList = distinctParameterList
.stream().map(temp -> new Parameter().paramName(temp.getParamName())
.paramValue(temp.getParamValue())).collect(Collectors.toList());
context.setParameters(distinctParametersList);
}
private static void removeDuplicatesVariables(Context context) {
final List<ParametersBase> parameterList = emptyIfNull(
context.getVariables()).stream()
.map(parameter -> new ParametersBase()
.paramName(parameter.getParamName())
.paramValue(parameter.getParamValue()))
.collect(Collectors.toList());
List<ParametersBase> distinctParameterList = removeDuplicate(parameterList);
List<Variable> distinctParametersList = distinctParameterList
.stream().map(temp -> new Variable().paramName(temp.getParamName())
.paramValue(temp.getParamValue())).collect(Collectors.toList());
context.setVariables(distinctParametersList);
}
As you see, the two methods which I created are almost the same, but since I have 2 auto-generated classes I have to duplicate the code. Is there a way to make the code prettier? I'm using Java 8.
You can generify removeDuplicate method:
private static <T> List<T> removeDuplicate(
Collection<? extends T> parameterList,
Function<? super T, String> name,
Function<? super T, String> value
) {
return new ArrayList<>(parameterList.stream().collect(
Collectors.<T, String, T>toMap(
name,
Function.identity(),
(a, b) -> mergeDuplicate(a, b, value)
)).values()
);
}
private static <T> T mergeDuplicate(T a, T b, Function<? super T, String> value) {
if (value.apply(a).equals(value.apply(b))) return a;
else throw new IllegalArgumentException("Error message");
}
And use it like this:
private static void removeDuplicatesParams(Context context) {
context.setParameters(removeDuplicate(
context.getParameters(),
Parameter::getParamName,
Parameter::getParamValue
));
}
Or you can apply the adapter design pattern:
private interface ParameterAdapter<T> {
T original();
String name();
String value();
}
private static <T> List<T> removeDuplicate(
Collection<? extends T> parameterList,
Function<? super T, ? extends ParameterAdapter<T>> adapter
) {
return parameterList.stream()
.<ParameterAdapter<T>>map(adapter)
.collect(Collectors.toMap(
ParameterAdapter::name,
Function.identity(),
Utils::mergeDuplicate
)).values().stream()
.map(ParameterAdapter::original)
.collect(Collectors.toList());
}
private static <T> ParameterAdapter<T> mergeDuplicate(
ParameterAdapter<T> a,
ParameterAdapter<T> b
) {
if (a.value().equals(b.value())) return a;
else throw new IllegalArgumentException("Error message");
}
And use it like this:
private static void removeDuplicatesParams(Context context) {
context.setParameters(removeDuplicate(
context.getParameters(),
Utils::adaptParameter
));
}
private static ParameterAdapter<Parameter> adaptParameter(Parameter parameter) {
return new ParameterAdapter<Parameter>() {
#Override
public Parameter original() { return parameter; }
#Override
public String name() { return parameter.getParamName(); }
#Override
public String value() { return parameter.getParamValue(); }
};
}
To be precise, I am trying to flatten a tree and I am stuck on trying to get the values of private attributes in a generic class using a generic function.
I have attached the classes to show how the tree is structured exactly. But it's looks something like this:
/|\
1 | 6
/|\
5 4 9
I am going to paste my attempt at the end. First, let me introduce the classes:
Triple simply stores three values of the same type.
public class Triple<V> {
private final V l, m, r;
public Triple(V l, V m, V r) {
this.l = l;
this.m = m;
this.r = r;
}
public V left() { return l; }
public V middle() { return m; }
public V right() { return r; }
}
Straightforward interface:
public interface Function<P, R> {
R apply(P p);
}
Now, for a tricky class. This one is simply a type that stores one of EitherOr of two types of value, but not both.
public class EitherOr<A,B> {
// Constructs a left-type EitherOr
public static <A> EitherOr left(A a) {
return new EitherOr(a, null);
}
// Constructs a right-type EitherOr
public static <B> EitherOr right(B b) {
return new EitherOr(null, b);
}
private final A a;
private final B b;
private EitherOr(A a, B b) {
this.a = a; this.b = b;
}
public<T> T ifLeft(Function<A,T> f) {
return f.apply(a);
}
public<T> T ifRight(Function<B,T> f) {
return f.apply(b);
}
public boolean isLeft() {
return b == null;
}
}
I know this is getting long, but bear with me. This class implements the tree structure.
public interface Tree<T> {
EitherOr<T, Triple<Tree<T>>> get();
static final class Leaf<T> implements Tree<T> {
public static <T> Leaf<T> leaf (T value) {
return new Leaf<T>(value);
}
private final T t;
public Leaf(T t) { this.t = t; }
#Override
public EitherOr<T, Triple<Tree<T>>> get() {
return EitherOr.left(t);
}
}
static final class Node<T> implements Tree<T> {
public static <T> Tree<T> tree (T left, T middle, T right) {
return new Node<T>(Leaf.leaf(left), Leaf.leaf(middle), Leaf.leaf(right));
}
private final Triple<Tree<T>> branches;
public Node(Tree<T> left, Tree<T> middle, Tree<T> right) {
this.branches = new Triple<Tree<T>>(left, middle, right);
}
#Override
public EitherOr<T, Triple<Tree<T>>> get() {
return EitherOr.right(branches);
}
}
}
Alright. Here is my idea for flattening:
public class MyFlattenTree<T> implements FlattenTree<T> {
public List<T> flattenInOrder(Tree<T> tree) {
List<T> list = new ArrayList<T>();
EitherOr<T, Triple<Tree<T>>> EitherOr;
EitherOr = tree.get();
// it is a leaf
if (EitherOr.isLeft()) {
// This is where the problem lies
// I don't how to get the value using a function f
list.add((T) EitherOr.ifLeft(f));
return list;
}
else {
// basically recursively go through the tree somehow
}
return null;
}
}
As I said, I am stuck with trying to retreive the value in the EitherOr class using the Function interface. I am thinking of implementing the Function interface and write a function for "apply" that just gets the value, but I am not sure how to do that. Any help would be appreciated. Thanks!
So, here is your flattenInOrder method:
public List<T> flattenInOrder(final Tree<T> tree) {
final EitherOr<T, Triple<Tree<T>>> EitherOr = tree.get();
if (EitherOr.isLeft()) {
return Collections.singletonList(EitherOr.ifLeft(this.ifLeftFunction));
}
return EitherOr.ifRight(this.ifRightFunction);
}
Quite simple, assuming that:
ifLeftFunction yields a single element (since EitherOr<T, Triple<Tree<T>>> has a single T elem' if it s "left")
... and:
ifRightFunction yields a collection of elements (since EitherOr<T, Triple<Tree<T>>> has a list of T elems' if it is "right")
Let's look into these functions now:
ifLeftFunction is... basic. I want to extract a T from... a T.
final Function<T, T> ifLeftFunction = new Function<T, T>() {
#Override
public T apply(final T t) {
return t;
}
};
ifRightFunction is slightly more complex: it has to be recursive and collect all Ts from the Tree it's browsing:
final Function<Triple<Tree<T>>, List<T>> ifRightFunction = new Function<Triple<Tree<T>>, List<T>>() {
#Override
public List<T> apply(final Triple<Tree<T>> t) {
final List<T> res = new ArrayList<>();
res.addAll(MyFlattenTree.this.flattenInOrder(t.left()));
res.addAll(MyFlattenTree.this.flattenInOrder(t.middle()));
res.addAll(MyFlattenTree.this.flattenInOrder(t.right()));
return res;
}
};
And... you're done!
Sample working code:
public class MyFlattenTree<T> {
private final Function<Triple<Tree<T>>, List<T>> ifRightFunction = new Function<Triple<Tree<T>>, List<T>>() {
#Override
public List<T> apply(final Triple<Tree<T>> t) {
final List<T> res = new ArrayList<>();
res.addAll(MyFlattenTree.this.flattenInOrder(t.left()));
res.addAll(MyFlattenTree.this.flattenInOrder(t.middle()));
res.addAll(MyFlattenTree.this.flattenInOrder(t.right()));
return res;
}
};
private final Function<T, T> ifLeftFunction = new Function<T, T>() {
#Override
public T apply(final T t) {
return t;
}
};
public static void main(final String[] args) {
final Tree<String> tree = new Node<>(new Leaf<>("1"), new Node<>(new Leaf<>("5"), new Leaf<>("4"), new Leaf<>("9")), new Leaf<>("6"));
System.out.println(new MyFlattenTree<String>().flattenInOrder(tree));
}
public List<T> flattenInOrder(final Tree<T> tree) {
final EitherOr<T, Triple<Tree<T>>> EitherOr = tree.get();
if (EitherOr.isLeft()) {
return Collections.singletonList(EitherOr.ifLeft(this.ifLeftFunction));
}
return EitherOr.ifRight(this.ifRightFunction);
}
}
Note that I'm creating the exact Tree you're featuring as an example in your question in the main method here:
public static void main(final String[] args) {
final Tree<String> tree = new Node<>(new Leaf<>("1"), new Node<>(new Leaf<>("5"), new Leaf<>("4"), new Leaf<>("9")), new Leaf<>("6"));
System.out.println(new MyFlattenTree<String>().flattenInOrder(tree));
}
Output: [1, 5, 4, 9, 6]
Cheers ;)
I have a method which looks like this:
void foo (List<String> list, ...) {
...
for (String s : list) { // this is the only place where `list` is used
...
}
...
}
the exact same code would work if I replace List<String> list with String[] list, however, to avoid spaghetti code, I keep the single method, and when I need to call it on an array a, I do it like this: foo(Arrays.asList(a)).
I wonder if this is The Right Way.
Specifically,
What is the overhead of Arrays.asList()?
Is there a way to write a method which would accept both arrays and lists, just like the for loop does?
Thanks!
Arrays.asList() has a small overhead. There is no real way to implement one method for both List and arrays.
But you can do the following:
void foo (List<String> list, ...) {
...
for (String s : list) { // this is the only place where *list* is used
...
}
...
}
void foo (String[] arr, ...) {
if ( arr != null ) {
foo(Arrays.asList(arr),...);
}
}
From the source code of openjdk, Arrays.asList:
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
furthermore:
ArrayList(E[] array) {
if (array==null)
throw new NullPointerException();
a = array;
}
So basically all that happens in an assignment, so the overhead should be negligible.
The overhead is that it converts an array to a list--how it does so would be implementation-dependent, it only needs to fulfill the contract.
IMO you should write two methods if you're concerned about the potential runtime overhead: that is the nature of Java; methods have type signatures, and they must be obeyed.
Do avoid this I just use and allow Lists, Sets and Maps (like Joshua Bloch told us). There is no way to merge both "collection types".
An alternative is to use guava (Iterators/Iteratables). So you can iterarte over your collections without a deep copy of them.
Good question.
This is a very common case, and is often dealt with by writing two separate methods. However code duplication is really a bad idea, and whenever you find yourself duplicating code, you should start looking for opportunities to factor your code better. (As you are doing right now!)
Now if you look into the source of java.util.Arrays, you will notice that Arrays.asList retruns an instance of a private inner class Arrays.ArrayList which is just a thin wrapper over plain arrays, and delegates all relevant method calls to it. (This is known as a projection or view of a data structure.) Therefore the overhead incurred is insignificant (unless you are striving to extract every last bit of performance), and in my opinion, you should go ahead and use this method without worrying about performance.
The solution I personally use is as follows.
I have a class named RichIterable in my personal utils. As the name indicates the class wraps over Iterable and provides some additional useful methods not already present. The class also has a factory method that creates an RichIterable from an array. Here is the class definition.
public class RichIterable<A> implements Iterable<A> {
private Iterable<A> xs;
private RichIterable(Iterable<A> xs) {
this.xs = xs;
}
public static <A> RichIterable<A> from(Iterable<A> xs) {
if (xs instanceof RichIterable) {
return (RichIterable<A>) xs;
} else {
return new RichIterable<A>(xs);
}
}
public static <A> RichIterable<A> from(final Enumeration<A> xs) {
Iterable<A> iterable = new Iterable<A>() {
#Override
public Iterator<A> iterator() {
return new Iterator<A>() {
#Override
public boolean hasNext() {
return xs.hasMoreElements();
}
#Override
public A next() {
return xs.nextElement();
}
#Override
public void remove() {
throw new UnsupportedOperationException(
"Cannot remove an element from an enumeration.");
}
};
}
};
return RichIterable.from(iterable);
}
public static <A> RichIterable<A> from(final A[] xs) {
Iterable<A> iterable = new Iterable<A>() {
#Override
public Iterator<A> iterator() {
return new Iterator<A>() {
private int i = 0;
#Override
public boolean hasNext() {
return i < xs.length;
}
#Override
public A next() {
A x = xs[i];
i++;
return x;
}
#Override
public void remove() {
throw new UnsupportedOperationException(
"Cannot remove an element from an array.");
}
};
}
};
return RichIterable.from(iterable);
}
public boolean isEmpty() {
if (xs instanceof Collection) {
return ((Collection) xs).isEmpty();
}
for (A x : xs) {
return false;
}
return true;
}
public int size() {
if (xs instanceof Collection) {
return ((Collection) xs).size();
}
int size = 0;
for (A x : xs) {
size++;
}
return size;
}
public ArrayList<A> toArrayList() {
ArrayList<A> ys = new ArrayList<A>();
for (A x : xs) {
ys.add(x);
}
return ys;
}
public <B> RichIterable<B> map(F1<A, B> f) {
List<B> ys = new ArrayList<B>();
for (A x : xs) {
ys.add(f.apply(x));
}
return RichIterable.from(ys);
}
public RichIterable<A> filter(F1<A, Boolean> pred) {
List<A> ys = new ArrayList<A>();
Arrays.asList();
for (A x : xs) {
if (pred.apply(x)) {
ys.add(x);
}
}
return RichIterable.from(ys);
}
public boolean exists(F1<A, Boolean> pred) {
for (A x : xs) {
if (pred.apply(x)) {
return true;
}
}
return false;
}
public boolean forall(F1<A, Boolean> pred) {
for (A x : xs) {
if (!pred.apply(x)) {
return false;
}
}
return true;
}
public Maybe<A> find(F1<A, Boolean> pred) {
for (A x : xs) {
if (pred.apply(x)) {
return Just.of(x);
}
}
return Nothing.value();
}
public String mkString(String beg, String sep, String end) {
Iterator<A> i = xs.iterator();
if (!i.hasNext()) {
return beg + end;
}
StringBuilder sb = new StringBuilder();
sb.append(beg);
while (true) {
A e = i.next();
sb.append(e.toString());
if (!i.hasNext()) {
return sb.append(end).toString();
}
sb.append(sep);
}
}
public String mkString(String sep) {
return mkString("", sep, "");
}
public String mkString() {
return this.mkString(", ");
}
public Iterable<A> getRaw() {
return xs;
}
#Override
public Iterator<A> iterator() {
return xs.iterator();
}
}
Here's a class I wrote that implements Iterable<Integer> for an arithmetic series (from start to stop in steps of step)
package com.example.test;
import java.util.Iterator;
import com.google.common.collect.AbstractIterator;
public class ArithmeticSeries implements Iterable<Integer>
{
final private int start, step, stop;
public int getStart() { return this.start; }
public int getStep() { return this.step; }
public int getStop() { return this.stop; }
public ArithmeticSeries(int start, int step, int stop)
{
this.start = start;
this.step = step;
this.stop = stop;
}
#Override public Iterator<Integer> iterator()
{
return new AbstractIterator<Integer>() {
private Integer n = null;
#Override protected Integer computeNext() {
int next;
if (this.n == null)
{
next = getStart();
}
else
{
next = this.n + getStep();
if ((getStep() > 0 && next > getStop())
|| (getStep() < 0 && next < getStop()))
return endOfData();
}
this.n = next;
return next;
}
};
}
#Override public String toString() {
return getStart()+":"+getStep()+":"+getStop();
}
public static void main(String[] args) {
Iterable<Integer> range = new ArithmeticSeries(100,-1,80);
System.out.println(range);
for (int i : range)
System.out.println(i);
}
}
Is there a way to implement iterator() that's more elegant? I don't like the null check and use of Integer (alternative would be an extra flag boolean firstTime), it just seems wrong.
return new AbstractIterator<Integer>() {
int next = getStart();
#Override protected Integer computeNext() {
if (isBeyondEnd(next)) {
return endOfData();
}
Integer result = next;
next = next + getStep();
return result;
}
};
If you wanted to, you could probably implement this as an immutable List<Integer>. If you extend AbstractList then the Iterator would be taken care of for you. Actually, I think AbstractList would really be the best way to go. The whole class would look like something like this (I haven't checked that it works right in all situations):
public class ArithmeticSeries extends AbstractList<Integer> {
private final int start;
private final int step;
private final int size;
public ArithmeticSeries(int start, int end, int step) {
this.start = start;
this.step = (start < end) ? step : -step;
this.size = (end - start) / this.step + 1;
}
#Override public Integer get(int index) {
return start + step * index;
}
#Override public int size() {
return size;
}
}
You can use a Function to abstract the successive values and a Predicate to control the end of iteration, eventually creating an Unfold implementation:
public final class UnfoldIterator<E> implements Iterator<E> {
public static <E> Iterator<E> unfold(E initial, Function<? super E, ? extends E> next, Predicate<? super E> finished) {
return new UnfoldIterator<E>(initial, next, finished)
}
private final Function<? super E, ? extends E> next;
private final Predicate<? super E> finished;
private E element;
public UnfoldIterator(E initial, Function<? super E, ? extends E> next, Predicate<? super E> finished) {
super();
this.next = next;
this.finished = finished;
this.element = initial;
}
#Override protected Integer computeNext() {
if (finished.apply(element)) {
return endOfData();
}
E result = element;
element = next.apply(element);
return result;
}
}
Then ArithmeticSeries becomes:
public Iterable<Integer> series(final int start, final int step, final int stop) {
return new Iterable<Integer>() {
public Iterator<Integer> iterator() {
return new UnfoldIterator<Integer>(start, new Function<Integer, Integer>() {
public Integer apply(Integer from) {
return from - step;
}
}, new Predicate<Integer>() {
public boolean apply(Integer input) {
return input >= stop;
}
});
}
};
}
Of course the code seems more complex now, but with appropriate base functions for comparison and algebra the call becomes much clearer:
return unfold(start, subtractBy(step), not(lessThan(stop)));
I think the best tool for your problem in guava is the AbstractLinkedIterator. Implementation of your example would look like this:
final Iterator<Integer> series = new AbstractLinkedIterator<Integer>(100) {
#Override protected Integer computeNext(final Integer previous) {
return previous == 80 ? null : previous - 1;
}
};
while (series.hasNext()) {
System.out.println(series.next());
}
You can easily create an Iterable adapter for this iterator, e.g. like this:
package sk.the0retico.guava;
import java.util.Iterator;
import com.google.common.base.Function;
import com.google.common.collect.AbstractLinkedIterator;
public class LinkedIterable<T> implements Iterable<T> {
public static final <T> Iterable<T> from(final T first,
final Function<T, T> computeNext) {
return new LinkedIterable<T>(first, computeNext);
}
public static void main(final String[] args) {
final Iterable<Integer> series = LinkedIterable.from(100,
new Function<Integer, Integer>() {
#Override public Integer apply(final Integer input) {
return input == 80 ? null : input - 1;
}
});
for (final Integer value : series) {
System.out.println(value);
}
}
private final Function<T, T> computeNext;
private final T first;
public LinkedIterable(final T first, final Function<T, T> computeNext) {
this.first = first;
this.computeNext = computeNext;
}
#Override public Iterator<T> iterator() {
return new AbstractLinkedIterator<T>(first) {
#Override protected T computeNext(final T previous) {
return computeNext.apply(previous);
}
};
}
}
However this approach makes special constraints on the provided function returning null.