Subato

Unveränderbare einfach verkettete Liste

package name.panitz.util;

import java.util.Collection;
import java.util.Comparator;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;

public class LL<A>{
  private final A hd;
  private final LL<A> tl;

  public boolean isEmpty() {
    return hd == null && tl == null;
  }

  public LL(A hd, LL<A> tl) {
    this.hd = hd;
    this.tl = tl;
  }

  public LL() {
    this(null, null);
  }

  public A get(int i) {
    return i == 0 ? hd : tl.get(i - 1);
  }

  public int size() {
    if (isEmpty())
      return 0;
    return 1 + tl.size();
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((hd == null) ? 0 : hd.hashCode());
    result = prime * result + ((tl == null) ? 0 : tl.hashCode());
    return result;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj) {
      return true;
    }
    if (obj == null) {
      return false;
    }
    if (!(obj instanceof LL)){
      return false;
    }
    LL other = (LL) obj;
    if (hd == null) {
      if (other.hd != null) {
        return false;
      }
    } else if (!hd.equals(other.hd)) {
      return false;
    }
    if (tl == null) {
      if (other.tl != null) {
        return false;
      }
    } else if (!tl.equals(other.tl)) {
      return false;
    }
    return true;
  }

  @SuppressWarnings("unchecked")
  static <A> LL<A> create(A... es){
    LL<A> result = new LL<A>();
    for (int i=es.length-1;i>=0;i--){
      result = new LL<A>(es[i],result);
    }
    return result;  
  }

  
  public boolean containsWith(Predicate<? super A> p) {
    if (isEmpty())
      return false;
    if (p.test(hd))
      return true;
    return tl.containsWith(p);
  }

  boolean contains(A el) {
    return containsWith(x->x.equals(el));
  }
  
  boolean isPrefixOf(LL<A> that) {
    if (isEmpty())return true;
    if (that.isEmpty()) return false;
    return hd.equals(that.hd)&&tl.isPrefixOf(that.tl);
  }


  LL<A> drop(int i){
    if (i<=0||isEmpty()) return this;
    return tl.drop(i-1);
  }
    
  LL<A> take(int i){
    if (i<=0||isEmpty()) return new LL<>();
    return new LL<>(hd,tl.take(i-1));
  }

  public LL<A> sublist(int from, int length) {
    return drop(from).take(length);
  /*    if (isEmpty())
      return new LL<>();
    if (length == 0)
      return new LL<>();
    if (from == 0) {
      return new LL<A>(hd, tl.sublist(0, length - 1));
    }
    return tl.sublist(from - 1, length);*/
  }

  A last(){
    if (isEmpty())return null;
    if (tl.isEmpty()) return hd;
    return tl.last();
  }
  
  LL<A> append(LL<A> that){
    if (isEmpty()) return that;
    return new LL<>(hd,tl.append(that));
  }

  public void forEach(Consumer<? super A> con) {
    if (isEmpty())
      return;
    con.accept(hd);
    tl.forEach(con);
  }

  LL<A> filter(Predicate<? super A>  p){
    if (isEmpty())
      return new LL<>();
    if (p.test(hd))
      return new LL<>(hd, tl.filter(p));
    return tl.filter(p);
  }
  

  <B> LL<B> map(Function<? super A, ? extends B> f){
    if (isEmpty())
      return new LL<>();
    return new LL<>(f.apply(hd), tl.map(f));
  }

  LL<A> reverse2(){
    if (isEmpty())return new LL<A>();
    return tl.reverse().append(new LL<>(hd,new LL<>()));
  }

  LL<A> reverse(){
    var result = new LL<A>();
    for (var it = this;!it.isEmpty();it=it.tl){
      result = new LL<>(it.hd,result);
    }
    return result;

  }

  @Override 
  public String toString(){
    StringBuffer result = new StringBuffer("[");
    boolean first = true;
    for (LL<A> it = this;!it.isEmpty();it=it.tl){
      if (first){
        first = false;
      } else{
        result.append(", ");
      }
      result.append(it.hd);
    }
    result.append("]");
    return result.toString();
  }
}