Reasons why Java sucks compared to C#

C#
Java
Generics can't hold primitive types e.g. int.
C#
List<int>
Java
ArrayList<Integer>
You can't use square brackets (indexers) to get and set elements in anything other than arrays.
C#
intList[i] = 3;
dict["hi"] += 4;
Java
intList.set(i, 3);
dict.set("hi", dict.get("hi") + 4);
Streams are more cumbersome than Linq.
C#
string.Join(", ",
  ints.Where(i => i % 2 == 0)
);
Java
ints.stream()
  .filter(i -> i % 2 == 0)
  .mapToObj(Integer::toString)
  .collect(Collectors.joining(", "));
Arrays don't implement the List interface.
C#
void PrintElements(IList<int> lst) {
  // lst is an array/List/etc.
  foreach (int x in lst) {
    Console.WriteLine(x);
  }
}
Java
void printElements(List<Integer> lst) {
  // lst can't be an array.
  for (int x : lst) {
    System.out.println(x);
  }
}

void printElements(Integer[] lst) {
  for (int x : lst) {
    System.out.println(x);
  }
}
No primitive unsigned byte type. Seriously, what? How often are signed bytes useful?
C#
byte[] bs = GetSomeBytes();

foreach (byte b in bs) {
  Console.WriteLine(
    "Unsigned byte: " + b);
}
Java
byte[] bs = getSomeBytes();

for (byte b : bs) {
  int ub = (int) b & 0xff;
  System.out.println(
    "Unsigned byte: " + ub);
}
No property support.
C#
public double Hours
{
  get { return seconds / 3600; }
  set { seconds = value * 3600; }
}
Java
public double getHours() {
  return seconds / 3600;
}

public void setHours(double hours) {
  seconds = hours * 3600;
}
C#
// (Enable overflow checking in
// compiler options.)

int a = int.MaxValue
// Throws OverflowException.
a = a + 1
Java
int a = Integer.MAX_VALUE;
// Assigns -2147483648 to a.
a = a + 1;
No reference/output parameters.
C#
int num;
if (int.TryParse("2", out num)) {
  Console.WriteLine(num);
}
Java
Integer num = null;
try {
  num = Integer.parseInt("2");
} catch (NumberFormatException ex) {
}

if (num != null) {
  System.out.println(num);
}
C#
void PrintNum(int i, string message = "Num") {
  Console.WriteLine($"{message}: {i}");
}
Java
void printNum(int i, String message) {
  System.out.printf("%s: %d\n", message, i);
}

void printNum(int i) {
  printNum(i, "Num");
}
C#
public class Main {
  public static void Main(string[] args) {
    Vector v1 = new Vector(1, 2);
    Vector v2 = new Vector(2, 1);

    Vector v3 = v1 + v2;
  }
}

public class Vector {
  //...

  public static Vector operator +(
      Vector v1, Vector v2) {
    return new Vector(v1.X + v2.X, v1.Y + v2.Y);
  }
}
Java
public class Main {
  public static void main(String[] args) {
    Vector v1 = new Vector(1, 2);
    Vector v2 = new Vector(2, 1);

    Vector v3 = v1.add(v2);
  }
}

public class Vector {
  //...

  public Vector add(
      Vector v1, Vector v2) {
    return new Vector(v1.x + v2.x, v1.y + v2.y);
  }
}