Поради щодо гольфу на Яві


86

Чи є корисні ярлики, які можна використовувати на Java?

Як показано нижче, у програму importвже додано щонайменше 17 символів.

import java.io.*;

Я розумію, що простим рішенням було б використання іншої мови, але, здається, це справжнє завдання скоротити програми Java.


Поради повинні бути специфічними для Java: якщо вони застосовні для більшості мов, подібних С, вони належать до більш загального списку порад .


9
packageможна пропустити.
st0le

У відповідь, чи не можу я просто опустити імпорт, якщо припустити, що він є?
Фабрикіо

1
@Fabricio Якщо не зазначено в ОП.
nyuszika7h

32
Найкраща порада про гольф на Java: не використовуйте її. ;)
kirbyfan64sos

4
"Хочу в гольф в java" удачі
sagiksp

Відповіді:


85
  • Використовуйте останню можливу Java. Java 8 дозволяє використовувати лямбда-вирази, тому використовуйте їх, якщо вам потрібне щось, навіть як функціональні об'єкти.

  • Визначте скорочені функції для речей, якими ви багато користуєтесь. Наприклад, у вас сто викликів exampleClassInstance.doSomething(someParameter), визначте нову функцію void d(ParameterType p){exampleClassInstance.doSomething(p)}та використовуйте її, щоб зберегти собі кілька символів.

  • Якщо ви використовуєте певну назву довгого класу не один раз, наприклад

    MyEventHandlerProxyQueueExecutorServiceCollectionAccessManagerFactory

    замість цього визначте новий клас:

    class X extends MyEventHandlerProxyQueueExecutorServiceCollectionAccessManagerFactory{}

    Якщо ви використовуєте лише один конкретний метод цього класу (але його ще потрібно інстанціювати), ви можете одночасно визначити скорочену версію всередині нового класу.

  • Використовуйте параметри типу функції, щоб скоротити речі, де це можливо, наприклад:

    <T>void p(T o){System.out.println(o);}
  • Використовуйте for(;;)замість while(true).

  • Не використовуйте модифікатори доступу, якщо це абсолютно не потрібно.

  • Не використовуйте finalні для чого.

  • Ніколи не кладіть блок після forциклу (але петля for(x:y)передбачення відрізняється). Додаткові твердження повинні бути розміщені всередині самого forтвердження, наприклад for(int i=0;i<m;a(i),b(++i))c(i);.

  • Використовуйте вбудоване присвоєння, збільшення та інстанціювання. Використовуйте анонімні вбудовані класи, де це доречно. Замість цього використовуйте лямбда, якщо це можливо. Виклики функції гніздо. Деякі функції гарантовано повертають батьківський об'єкт, ці фактично навіть призначені для того, щоб бути пов'язаними разом.

  • Ваш mainметод throws Exceptions, а не ловить їх.

  • Errorкоротше, ніж Exception. Якщо з якихось причин вам справді потрібно throwнадсилати повідомлення стеком, використовуйте Error, навіть якщо це абсолютно нормальна ситуація.

  • Якщо якась умова потребує негайного припинення, використовуйте, int a=1/0;а не throw null;або System.exit(0);. Під час виконання, це кидає ArithmeticException. Якщо у вас уже є числова змінна в коді, використовуйте її замість неї. (Якщо у вас вже є import static java.lang.System.*;, ідіть exit(0);.)

  • Замість того, щоб реалізувати інтерфейси, наприклад List<E>, розширити негайний (або не дуже безпосередній, якщо є взагалі якась перевага для цього) дочірнього класу, як AbstractList<E>, який забезпечує реалізацію за замовчуванням більшості методів і вимагає лише реалізації кілька ключових творів.

  • Спершу випишіть свій код з долоні, з новими рядками, відступами та повними назвами змінних. Коли у вас є робочий код, ви можете скоротити імена, переміщувати декларації навколо та додавати способи швидкого доступу. Виписуючи це довго, щоб почати, ви даєте собі більше можливостей спростити програму в цілому.

  • Порівняйте альтернативні оптимізації з фрагментом коду, оскільки найоптимальніша стратегія може кардинально змінитися при дуже невеликих змінах коду. Наприклад:

    • Якщо у вас є тільки до двох викликів Arrays.sort(a), найбільш ефективним способом є виклик його з повним ім'ям, java.util.Arrays.sort(a).
    • З трьома і більше викликами ефективніше замість цього додати метод швидкого доступу void s(int[]a){java.util.Arrays.sort(a);}. У цьому випадку все ж слід використовувати повноцінне ім’я. (Якщо вам потрібно більше ніж одне перевантаження, ви, ймовірно, робите це неправильно.)
    • Однак якщо ваш код також повинен скопіювати масив у якийсь момент (зазвичай це робиться з коротким forциклом у гольфі, за відсутності легкодоступного бібліотечного методу), ви можете скористатися, Arrays.copyOfщоб виконати завдання. Якщо використовується більше одного методу і є 3 або більше дзвінків, import static java.util.Arrays.*;це найефективніший спосіб звернення до цих методів. Після цього, лише якщо у вас більше 8 окремих дзвінків, sortви повинні використовувати для нього метод швидкого доступу, і лише для 5 або більше викликів це ярлик гарантовано copyOf.

    Єдиний реальний спосіб проведення такого аналізу на коді - це реально виконати потенційні зміни на копіях коду, а потім порівняти результати.

  • Уникайте використання someTypeValue.toString();методу, а просто додайте someTypeValue+"".

  • Якщо вам потрібні вікна, не використовуйте Swing, використовуйте AWT (якщо вам справді щось не потрібно від Swing). Порівняйте import javax.swing.*;і import java.awt.*;. Крім того, компоненти Swing , мають Jвипереджати їх ім'я ( JFrame, JLabel, і т.д.), але компоненти AWT немає ( Frame, Labelі т.д.)


43

Використовуйте interfaceзамість class.

У java 8 до інтерфейсів додавали статичні методи. В інтерфейсах всі методи за замовчуванням є загальнодоступними. Отже

class A{public static void main(String[]a){}}

тепер можна скоротити до

interface A{static void main(String[]a){}}

що, очевидно, коротше.

Наприклад, я використовував цю функцію в Hello, World! виклик.


8
Я цього не знав! +1, приємний трюк
HyperNeutrino

Так, менше котла!
CalculatorFeline

3
Я частково не погоджуюся (також я переміг вас у виклику "Привіт, світ!", Використовуючи цю техніку).
Олів'є Грегоар

37

За допомогою varargs ви можете « накинути » параметр на масив одного типу:

void f(String...x){
    x=x[0].split("someregex");
    // some code using the array
}

замість

void f(String s){
    String[]x=s.split("someregex");
    // some code using the array
}

31

Зі статичним імпортом :

import static java.lang.System.out;
// even shorter (thanks to Johannes Kuhn):
import static java.lang.System.*;

пізніше ви можете зберегти деяку плиту, але для отримання виплати вам потрібно кілька викликів:

public static void main (String[] args) {
    out.println ("foo");    
    out.println ("bar");    
    out.println ("baz");    
}

8
: О. Ви можете це зробити ?! І весь цей час я думав, що це неможливо Java!
Джастін

12
можна навіть використовувати import static java.lang.System.*.
Йоганнес Кун

1
Я знаю, що це стара відповідь, але в Java 10 тепер можна робити, var o=System.out;яку потрібно використовувати лише двічі, перш ніж вона окупиться
Люк Стівенс

@LukeStevens: Ну, можливо, ви знайдете якісь інші вдосконалення, можливі в Java10, і сформуєте окрему відповідь навколо Java10?
користувач невідомий

1
@LukeStevens буде var o=System.out.printlnпрацювати?
MilkyWay90

25

Аргумент mainне потрібно викликати args, і ви можете вирізати деякий пробіл:

public static void main(String[]a){}

зробить просто чудово.


1
Чи потрібно відповіді включати в себе основну функцію, якщо в ній прямо не вказано написати повну програму? Я використовував лямбда-вирази як відповіді.
Макотосан

3
@Makotosan Ні, ні; Лямбди, як правило, добре.
Данієро

21

Якщо вам коли-небудь доведеться використовувати булеві вирази trueабо false, замініть їх відповідно 1>0і 1<0відповідно.

Наприклад:

boolean found=false;
for(i=0; i<10; i++) if(a[i]==42) found=true;

Цей приклад лінійного пошуку можна звести до

boolean f=1<0;
for(i=0;i<10;)if(a[i++]==42)f=1>0;

11
Якщо вам потрібно буде багато true/false, просто додайте boolean t=1>0,f=1<0;. Потім замість цього 1>0використовуйте tта зберігайте дві символи за кожне використання. 1>0Метод відшкодування надходить на 10 застосувань.
Геобіт

24
@Geobits: boolean t=1>0,f=!t;- один шар коротший!
боббел

6
Приклад не дуже хороший. У цьому випадку та багато (!) Інших людей ви можете уникнути використання true/ falseбезпосередньо у будь-якому випадку: f|=a[i++]==42;економить досить багато.
Інго Бюрк

@ IngoBürk Правда. Коли я писав це, я в основному думав про бібліотечні функції, які використовує boolean, але оскільки я не міг придумати жодних прикладів під час написання (я зазвичай не кодую на Java), я просто написав простий приклад.
ace_HongKongIndependence

1
@Geobits не надто знайомий з Java, але ви могли б просто визначити 1 і використовувати t і! T (знову я не знаю Java, просто цікаво)
Альберт Реншо

20

Якщо ви будете багато використовувати якийсь метод, призначте його резидентний клас змінній. Наприклад, призначити System.outзмінну:

java.io.PrintStream o=System.out;
//now I can call o.print() or o.println() to the same effect as System.out.println()

Також для Integer.parseInt():

Integer i=1;
i.parseInt("some string");

Це майже напевно спровокує ідею попередження про "доступ до статичного методу із змінної"


((Integer)1).parseInt("1")працює теж.
Чарівний восьминога Урна

5
@carusocomputing new Integer("1")ще коротше. Але те, що Джастін мав на увазі своєю відповіддю, - це те, що ти можеш використовувати повторно використані змінні для статичних дзвінків. Як я пояснюю внизу цієї відповіді.
Кевін Кройсейсен

19

Замість того, щоб використовувати import static java.lang.System.*техніку для збереження на println()операторах, я виявив, що визначення наступного методу набагато ефективніше для збереження символів:

static<T>void p(T p){
    System.out.println(p);
}

Це тому, що на нього можна звернутися як p(myString)швидше, ніж на те, out.println(myString)що має набагато швидший і драматичніший окуп.


19

Це може здатися очевидним, але існують більш короткі варіанти деяких Mathфункцій:

a=Math.max(b,c);
a=b>c?b:c;

a=Math.min(b,c);
a=b<c?b:c;

a=Math.abs(b);
a=b<0?-b:b;

a=Math.round(b);
a=(int)(b+.5);          // watch for precision loss if it matters

14

Якщо вам потрібно Integer.MAX_VALUE(2147483647), використовуйте -1>>>1. Integer.MIN_VALUE(-2147483648) краще писати 1<<31.


14

Якщо вам потрібно захопити число з аргументу (або будь-якого іншого рядка), зазвичай ви бачите щось на зразок:

public static void main(String[]a){
    int n=Integer.valueOf(a[0]);
    ...
}

Багато разів, вам не потрібноInteger . Безліч проблем не використовують велику кількість. Оскільки Shortі Byteбуде розблоковуватися доint , скористайтеся більш підходящим valueOf()замість цього і збережіть пару байтів.

Тримайте фактичну змінну як int, хоча, так як це коротший як byteі short:

int n=Byte.valueOf(a[0]);

Якщо вам потрібно зробити це для кількох чисел, ви можете комбінувати цей спосіб :

Byte b=1;
int n=b.valueOf(a[0]),m=b.valueOf(a[1])...

9
int n=new Byte(a[0]);на три коротші. Якщо число може бути більшим, використовуйте long n=new Long(a[0]), це все-таки краще, ніж ints у більшості випадків.
Ypnypn

14

Не використовуйте public class. Основний метод повинен бути загальнодоступним, але його клас не робить. Цей код працює:

class S{public static void main(String[]a){System.out.println("works");}}

Ви можете бігати, java Sхоча class Sце не громадський клас. ( Оновлення. Коли я писав цю пораду, я використовував Java 7. В Java 8 ваш основний метод повинен знаходитись в інтерфейсі . У Java 5 або 6 ваш основний метод повинен знаходитись у перерахунку .)

Чимало Java-програмістів цього не знають! Близько половини відповідей на питання переповнення стека щодо main у непублічному класі неправильно стверджують, що основний метод повинен бути у публічному класі. Тепер ти краще знаєш. Видаліть publicін public classі зберегти 7 символів.


1
Якщо ви не орієнтуєтесь на Java до 1.8, конструкція interface s{static void main(String[]...коротша. Якщо у вас є компільований вихідний файл та основний метод. Оскільки в інтерфейсі Java 1.8 усі методи є загальнодоступними, тому ви можете пропустити модифікатор на методах.
Дуглас Холд

Я нещодавно не використовував Java, тому моя відповідь застаріла. Я забув, що інтерфейси можуть мати методи в Java 8.
kernigh,

Я не навчився цього програмуванню; Я дізнався це з гольфу :)
Douglas Held

14

Деякі невеликі поради щодо гольфу з кодом

Ці поради були трохи замалі для розділених відповідей, тому я буду використовувати цю відповідь для дуже маленьких порад щодо кодового гольфу, які я знайшов або придумав, і в інших порадах поки не згадується:

Видалення останнього символу рядка:

// I used to do something like this:
s.substring(0,s.length()-1)     // 27 bytes

// But this is shorter:
s.replaceAll(".$","")           // 21 bytes

У деяких випадках ви заздалегідь знаєте, що є останнім символом, а також знаєте, що цей символ виникає лише один раз у рядку. У такому випадку ви можете використовувати .splitзамість цього:

// As example: "100%" to "100"
s.split("%")[0]                 // 15 bytes

Кодування ярликів:

// When you want to get the UTF-8 bytes I used to do this:
s.getBytes("UTF-8");     // 20 bytes

// But you can also use "UTF8" for the same result:
s.getBytes("UTF8");      // 19 bytes

Усі кодування мають канонічну назву, що використовується в java.nioAPI, а також канонічне ім'я, що використовується в API java.ioта java.langAPI. Ось повний список усіх підтримуваних кодувань на Java. Тому завжди використовуйте найкоротший з двох; другий зазвичай коротший (як UTF-8vs utf8, Windows-1252vs Cp1252тощо), але не завжди ( UTF-16BEvs UnicodeBigUnmarked).

Випадкові булі:

// You could do something like this:
new java.util.Random().nextBoolean()     // 36 bytes

// But as mentioned before in @Geobits' answer, Math.random() doesn't require an import:
Math.random()<.5                         // 16 bytes

Примери:

Існує дуже багато різних способів перевірити чистість чи отримати всі пролі , але відповідь @ SaraJ тут найкоротша. Ось копія-вставка як довідка:

// Check if n is a prime:
n->{int i=1;for(;n%++i%n>0;);return n==i;}

// Which can easily be modified to loop through primes:
v->{for(int n=2,i;;){for(i=1;n%++i%n>0;);if(n++==i)/*do something with prime `i` here*/;}}

ПРИМІТКА: Зазвичай ви можете об'єднати його з іншими існуючими циклами залежно від способу його використання, тому вам не знадобиться окремий метод. Наприклад, це врятувало багато байтів у цій відповіді .

Урізання цілого числа замість Math.floor / Math.ceil:

Якщо ви використовуєте додатні подвійні / поплавці, і ви хочете floorїх, не використовуйте, Math.floorа використовуйте (int)-cast замість цього (оскільки Java скорочує цілі числа):

double d = 54.99;

int n=(int)Math.floor(d);     // 25 bytes

int m=(int)d;                 // 13 bytes

// Outputs 54 for both

Цей же трюк можна застосувати до негативних парних чи поплавців, які ви хочете ceilзамість цього:

double d = -54.99;

int n=(int)Math.ceil(d);     // 24 bytes

int m=(int)d;                // 13 bytes

// Outputs -54 for both

Використовуйте &1замість%2 щоб позбутися дужок:

Оскільки оператор Позачерговий з &нижче арифметичних операторів по замовчуванням , як */+-і %ви можете позбутися від дужок в деяких випадках.

// So instead of this:
(i+j)%2     // 7 bytes

// Use this:
i+j&1       // 5 bytes

Зауважте, що це не дуже допомагає в булевих перевірях, оскільки тоді вам все одно знадобляться круглі дужки, вони просто трохи переміщаються:

(i+j)%2<1    // 9 bytes
(i+j&1)<1    // 9 bytes

BigIntegers та створення змінних для статичних викликів методу:

Використовуючи BigIntegers, створіть його лише один раз, який потім зможете повторно використовувати. Як ви знаєте, BigInteger містить статичні поля для ZERO, ONEі TEN. Тож, коли ви використовуєте лише ці три, вам не потрібно, importале можна використовувати java.Math.BigIntegerбезпосередньо.

// So instead of this:
import java.math.BigInteger.*;
BigInteger a=BigInteger.ONE,b=BigInteger.ZERO;                // 76 bytes

// or this:
java.math.BigInteger a=java.math.BigInteger.ONE,b=a.ZERO;     // 57 bytes

// Use this:
java.math.BigInteger t=null,a=t.ONE,b=t.ZERO;                 // 45 bytes                  

ПРИМІТКА. Для використання вам потрібно ініціалізувати =nullтак .tt.

Іноді ви можете додати кілька BigIntegers, щоб створити інший для збереження байтів. Тож скажімо, ви хочете 1,10,12чомусь мати BigIntegers :

// So instead of this:
BigInteger t=null,a=t.ONE,b=t.TEN,c=new BigInteger(12);     // 55 bytes

// Use this:
BigInteger t=null,a=t.ONE,b=t.TEN,c=b.add(a).add(a);        // 52 bytes

Як правильно зазначено в коментарях, трюк із BigInteger t=null;статичними викликами методу може бути використаний і для інших класів.
Наприклад, ця відповідь з 2011 року може бути гольф:

// 173 bytes:
import java.util.*;class g{public static void main(String[]p){String[]a=p[0].split(""),b=p[1].split("");Arrays.sort(a);Arrays.sort(b);System.out.print(Arrays.equals(a,b));}}

// 163 bytes
class g{public static void main(String[]p){java.util.Arrays x=null;String[]a=p[0].split(""),b=p[1].split("");x.sort(a);x.sort(b);System.out.print(x.equals(a,b));}}

getBytes() замість toCharArray()

Коли ви хочете перевести цикл на символів рядка, зазвичай це зробите:

for(char c:s.toCharArray())    // 27 bytes
// or this:
for(String c:s.split(""))      // 25 bytes

Перекидання символів може бути корисним під час друку або додавання їх до рядка або чогось подібного.

Однак, якщо ви використовуєте тільки символи для деяких розрахунків юнікода-номер, ви можете замінити charз int, і ви можете замінити toCharArray()з getBytes():

for(int c:s.getBytes())        // 23 bytes

Або навіть коротше в Java 8+:

s.chars().forEach(c->...)      // 22 bytes

У Java 10+ перегляд символу для друку також можна виконати в 22 байтах:

for(var c:s.split(""))         // 22 bytes

Випадковий елемент із List:

List l=...;

// When we have an `import java.util.*;` in our code, shuffling is shortest:
return l.get(new Random().nextInt(l.size()));     // 45 bytes
return l.get((int)(Math.random()*l.size()));      // 44 bytes
Collections.shuffle(l);return l.get(0);           // 39 bytes

// When we don't have an `import java.util.*` in our code, `Math.random` is shortest:
return l.get(new java.util.Random().nextInt(l.size()));     // 55 bytes
return l.get((int)(Math.random()*l.size()));                // 44 bytes
java.util.Collections.shuffle(l);return l.get(0);           // 49 bytes

Перевірте, чи містить рядок провідні / кінцеві пробіли

String s=...;

// I used to use a regex like this:
s.matches(" .*|.* ")     // 20 bytes
// But this is shorter:
!s.trim().equals(s)      // 19 bytes
// And this is even shorter due to a nice feature of String#trim:
s!=s.trim()              // 11 bytes

Чому це працює, коли !=на Strings слід перевіряти посилання замість значення на Java? Тому що String#trimповернеться « Копія цього рядка з передньої і задньої прогалини видаляються, або це рядок , якщо вона не має передньої або задньої білого простору . » Я використав це, після того, як хто - то запропонував мені це, в цьому моя відповідь .

Паліндром:

Щоб перевірити, чи струна є паліндром (маючи на увазі і парні, і непарні довжини струн), це найкоротше ( .containsпрацює тут, тому що ми знаємо, що і сама струна, і її зворотна форма мають однакову довжину):

String s=...;
s.contains(new StringBuffer(s).reverse())    // 41 bytes

.contains(...)замість .equals(...+"")подяки коментаря @assylias тут .

Або 0, або обидва - 0?

Я думаю, що більшість з них уже знає це: якщо ви хочете перевірити, чи один aчи bнуль, помножте натомість, щоб зберегти байти:

a==0|b==0    // 9 bytes
a*b==0       // 6 bytes

І якщо ви хочете перевірити, чи обоє, aі bнулі, ви можете використовувати побітові АБО або додати їх разом, якщо вони завжди позитивні:

a==0&b==0    // 9 bytes
(a|b)==0     // 8 bytes (if either `a`, `b` or both can be negative)
a+b<1        // 5 bytes (this only works if neither `a` nor `b` can be negative)

Четний = 1, непарний = -1; або навпаки

// even = 1; odd = -1:
n%2<1?1:-1        // 10 bytes
1-n%2*2           // 7 bytes

// even = -1; odd = 1:
n%2<1?-1:1        // 10 bytes
n%2*2-1           // 7 bytes

Причину цього я додав, побачивши k+(k%2<1?1:-1)в цій відповіді :

k+(k%2<1?1:-1)    // 14 bytes

// This would already have been shorter:
k%2<1?k+1:k-1     // 13 bytes

// But it can also be:
k%2*-2-~k         // 9 bytes

Петля nЧаси в Повній програмі

Якщо у нас є виклик, коли повноцінна програма є обов'язковою, і нам потрібно циклічно фіксувати певну кількість разів, ми можемо зробити наступне:

// instead of:
interface M{static void main(String[]a){for(int n=50;n-->0;)/*do something*/}}  // 78 bytes
// we could do:
interface M{static void main(String[]a){for(M m:new M[50])/*do something*/}}    // 76 bytes

Це ж стосується, коли ми повинні сприймати цей діапазон як вхідний:

interface M{static void main(String[]a){for(int n=new Byte(a[0]);n-->0;)/*do something*/}}  // 90 bytes
interface M{static void main(String[]a){for(M m:new M[new Byte(a[0])])/*do something*/}}    // 88 bytes

Кредит @JackAmmo в цьому коментарі .

try-нарешті замість try-catch (Виняток e) при поверненні та коли його використовувати

Якщо ви не можете використовувати, throws Exceptionале потрібно catchщось робити з цим перед поверненням, ви можете скористатися ним finally:

try{...}catch(Exception e){return ...;}    // 33 bytes
try{...}finally{return ...;}               // 22 bytes

Що стосується прикладу, коли потрібно використовувати try-catch, я можу посилатися на цю свою відповідь (кредит на непрямий гольф йде на @KamilDrakari ). У цьому виклику нам потрібно провести діагональне перетворення за матрицею NxM, тому ми маємо визначити, чи кількість стовпців чи кількість рядків є найнижчою як наш максимум у фор-циклі (що досить багата з точки зору байтів:) i<Math.min(a.length,a[0].length). Отже, просто перехопити ArrayIndexOutOfBoundsExceptionвикористання catch-finallyкоротше, ніж ця перевірка, і, таким чином, економиться байт:

int[] a = ...;

int r=0,i=0;for(;i<Math.min(a.length,a[0].length);)r=...i++...;return r;    // 66 bytes

int r=0,i=0;try{for(;;)r=...i++...;}finally{return r;}                      // 48 bytes

ПРИМІТКА: Це спрацювало лише через, return r;нарешті. Мені запропонували змінити першу клітинку, як @KamilDrakari зробив у своїй відповіді C #, щоб зберегти байти. Однак у Java це означає, що мені доведеться змінити його на m->{try{for(int i=1;;m[0][0]=f(m[0][0],m[i][i++]));}catch(Exception e){}}(73 байти), фактично збільшуючи кількість байтів, а не зменшуючи, якщо я міг би використовуватиfinally .

Math.pow (2, n)

Коли вам потрібно потужність 2, трохи розумніший підхід значно коротший:

(int)Math.pow(2,n)    // 16 bytes
(1<<n)                // 6 bytes

Поєднання бітових та логічних перевірок замість використання дужок

Я думаю , що це добре відомо тепер , що &і |може бути використаний замість &&і ||в Java (логічне) логічних перевірок. У деяких випадках ви все одно хочете використовувати &&замість того, &щоб запобігти помилкам, хоча index >= 0 && array[index].doSomething. Якщо тут &&буде змінено значення &, воно все одно буде оцінювати частину, де він використовує індекс у масиві, викликаючи ArrayIndexOutOfBoundsException, отже, використання &&у цьому випадку замість& .

Поки основи &&/ ||vs &/| на Java.

Коли ви хочете перевірити (A or B) and C, може здатися, що найкоротші можуть використовувати такі розрядні оператори:

(A|B)&C    // 7 bytes

Однак, оскільки бітові оператори мають перевагу оператора над логічними перевірками, ви можете комбінувати обидва, щоб зберегти байт тут:

A|B&&C     // 6 bytes

Використовуйте n+=...-nзамість(long)...

Якщо у лямбда у вас є довгий і вихідний, і, наприклад, при використанні Math.pow, ви можете зберегти байт, використовуючи n+=...-nзамість (long)....
Наприклад:

n->(long)Math.pow(10,n)    // 23 bytes
n->n+=Math.pow(10,n)-n     // 22 bytes

Це врятувало байти в цій відповіді шахти , і навіть два байта, комбінуючи -n-1в +~nв цій відповіді шахти .


Більш загально, для останньої точки ви можете отримати доступ / викликати статичні члени з нестатичного контексту, наприклад екземпляра об'єкта.
Покрій

1
Я не розумію вашої підвіски під стелею. Чому ви хочете стелі positive integers? Також я не впевнений, чи працює реалізація стелі .
Джонатан Фрех

1
since Java automatically floors on integers; Я думаю, що правильний термін - це укорочення , а не настил .
Джонатан Фрех

1
Ще одна стратегія PalindromeString t="";for(int i=s.length();--i>=0;t+=s.charAt(i));return s.equals(t);
Роберто Грем

1
@RobertoGraham я фактично скопіював свій оригінальний код від неправильного виклику .. Просто s.equals(new StringBuffer(s).reverse()+"")достатньо.
Kevin Cruijssen

11

Для гольфу, який не потребує введення, ви можете використовувати статичні блоки, і запустити його просто без будь-якого основного методу, просто компілюйте його з Java 6.

public class StaticExample{
    static {
        //do stuff
    }
}

1
Ви намагалися компілювати та запускати його? Цей блок запускається, коли клас завантажується завантажувачем класів. Але завантажувач класів нічого не завантажує, поки не дізнається про клас з основним методом.
Cruncher

@Cruncher Ви можете це обійти, розказавши javaв командному рядку / у файлі маніфесту, який клас потрібно завантажити.
AJMansfield

6
@Cruncher, це працювало з Java 6. Java 7 змінила спосіб її роботи.
Пітер Тейлор

1
Викидає виняток наприкінці, але це працює! Навіть на Java 7
stommestack

2
@JopVernooij Якщо ви не хочете, щоб виняток кидався вам на обличчя, ви можете system.exit (), але ви будете витрачати персонажів, жоден виклик з гольфу ніколи не просить вас уникати винятків;)
Fabinout

11

Ми всі знаємо про побитовий xor ( ^), але це також логічний xor.

Так (a||b)&&!(a&&b)просто стає a^b.

Тепер ми можемо використовувати xor.

Крім того , оператори, |а & також працюють , просто пам'ятайте, що пріоритет оператора змінюється.


5
Поки ви пам’ятаєте пріоритет, ви можете використовувати & і |також. Це може бути корисно, якщо ваші умови вже в дужках або якщо ви вже працюєте з булевими.
Геобіц

1
Якщо вам потрібен (значно) нижчий пріоритет, ви можете використовувати !=замість ^xor, а ==для xnor
Cyoce

11

Не потрібно користуватися Character.toLowerCase(char c). Замість використання (c|32). Замість Character.toUpperCase(char c)використання (c&~32). Це працює лише з літерами ASCII.


c|~32схильний до -1 ... краще використовувати c-32.
feersum

5
@feersum Це не спрацювало, якби ви хотіли зробити великі літери великої літери.
TheNumberOne

11

Перетворити рядок у число

Існує кілька способів перетворення рядка в числове значення:

String s = "12";

ABC.parseABC :

Short.parseShort(s); // 20 bytes
Integer.parseInt(s); // 20 bytes
Long.parseLong(s);   // 18 bytes

ABC.valueOf :

Short.valueOf(s);    // 17 bytes
Integer.valueOf(s);  // 19 bytes
Long.valueOf(s);     // 16 bytes

ABC.decode :

// Note: does not work for numeric values with leading zeros,
// since these will be converted to octal numbers instead
Short.decode(s);     // 16 bytes
Integer.decode(s);   // 18 bytes
Long.decode(s);      // 15 bytes

новий ABC :

new Short(s);        // 13 bytes
new Integer(s);      // 15 bytes
new Long(s);         // 12 bytes

Отже, для коду-гольфу найкраще використовувати конструктор при перетворенні рядка в числове значення.

Це ж стосується і Double; Float; і Byte.


Це не завжди застосовується, коли ви можете повторно використовувати вже наявний примітив як об’єкт.
Наприклад, скажімо, у нас є такий код:

// NOTE: Pretty bad example, because changing the short to int would probably be shorter..
//       but it's just an example to get the point across

short f(short i,String s){
  short r=new Short(s);  // 21 bytes
  ... // Do something with both shorts
}

Ви можете використовувати .decodeзамість коротшого конструктора, повторно використовуючи параметр як об’єкт:

short f(Short i,String s){   // Note the short parameter has changed to Short here
  short r=i.decode(s);   // 20 bytes
  ... // Do something with both shorts
}

10

Не використовуйте Random!

Взагалі, якщо вам потрібні випадкові числа, Randomце жахливий спосіб зробити це *. Набагато краще використовувати Math.random()замість цього. Для використання Randomвам потрібно зробити це (скажімо, нам потрібен int):

import java.util.*;
Random r=new Random();
a=r.nextInt(9);
b=r.nextInt(9);

Порівняйте це з:

a=(int)(Math.random()*9);
b=(int)(Math.random()*9);

і:

int r(int m){return(int)(Math.random()*m);}
a=r(9);
b=r(9);

Перший метод приймає 41+15nсимволи ( nце кількість дзвінків). Друга - 25nсимволи, а третя - 43+7n.

Отже, якщо вам це потрібно лише один раз або два, скористайтеся Math.random()методом inline . Для трьох або більше дзвінків ви заощадите за допомогою функції. Або один зберігає символи на першому використанні більш Random.


Якщо ви вже використовуєте Math.random()для цього double, пам’ятайте, що при чотирьох цілях використання це все-таки економія:

double r(){return Math.random();}

Для 33 символів ви заощадите 10 на кожному дзвінку r()


Оновлення

Якщо вам потрібно ціле число і хочете заощадити на кастингу, не кидайте його! Java автоматично відкидає, якщо ви робите операцію замість призначення. Порівняйте:

a=(int)(Math.random()*9);
a=9;a*=Math.random();

* Якщо вам не доведеться висівати PRNG для передбачуваних результатів. Тоді я не бачу великого шляху.


2
Не забувайте, Random#nextGaussianхоча.
Джастін

@Quincunx Щоправда, виконання математики для отримання нормального нормального розподілу втратить вам будь-які заощадження. Я просто зазначу це як виняток, який підтверджує правило;)
Геобіт

Зверніть увагу, що (int)(Math.random()*9)модуль має дуже малий ухил, оскільки Math.random()повертає 2 53 можливих значення, а 2 53 не є кратним 9. Імовірність кожного числа знаходиться в межах 1/9 плюс або мінус 5 / (9 * 2 ** 53), помилка настільки мала, що майже рівно 1/9.
kernigh

@kernigh Право, я використовував 9лише приклад, це може бути все, що завгодно. Я відносно впевнений, що nextInt()(або будь-який інший Randomметод) також є невеликий ухил, саме завдяки тому, як працює PRNG Java.
Геобіц

1
Щось пов'язане з тим, коли ви хочете випадкову булеву: замість цього new java.util.Random().nextBoolean()ви можете використовувати Math.random()<.5.
Kevin Cruijssen

7

Я не знаю, чи вважаєте ви цю "чисту" Java, але Processing" дозволяє створювати програми з невеликими початковими налаштуваннями (завершуються автоматично).

Для виходу з консолі ви можете мати щось таке просте, як:

println("hi"); //done

для графічного виведення, трохи більше:

void setup() {
  size(640,480);
}
void draw() {
  fill(255,0,0); //color used to fill shapes
  rect(50,50,25,25); //25x25 pixel square at x=50,y=50
}

1
+1 Відмінний ресурс! Я обов’язково пограю з цим.
Роб

Було б добре, якби я додав відповіді інших людей до цього? Або це перемагає мету вікі спільноти?
Роб

2
До речі, вам навіть зовсім не потрібно дзвонити size; за замовчуванням буде квадрат 100 на 100 пікселів. У більшості ОС рамки навколо нього будуть приблизно вдвічі більшими, з центром квадрата, а решта площі заповнена вмістом, знятим з робочого столу.
AJMansfield

1
Для графічного виводу, якщо вам не потрібна анімація, ви можете просто записати все поза setup()і draw()використовувати "статичний режим". Ви також можете використовувати шестизначні шістнадцяткові кольори, і інтерпретатор буде змінювати їх, що іноді окупається ( #FF8000< 255,128,0), а якщо ви використовуєте відтінки сірого, потрібно вказати лише одне число ( 255< 255,255,255)
quat

7

Скорочення повернення

Ви можете скоротити оператори повернення рядків на байт за допомогою:

return "something";

до

return"something";

І якщо вам трапиться повернути заяву з дужки, ви можете зробити те ж саме з ними:

return (1+taxRate)*value;

до

return(1+taxRate)*value;

Гадаю, цитати вважаються дужкими? Я насправді підхопив це через AppleScript, досить смішно, і подумав, що це, можливо, варто згадати.


1
Те саме стосується чисельних знаків, як-от return-n;замість return -n;або return~n;замість return ~n;. А також сингл замість подвійних цитат:return'A';
Кевін Круїйсен

1
В основному, працює для будь-якого, що не може бути частиною ідентифікатора (наприклад, безбуквеного та нецифрового).
Paŭlo Ebermann

7

Не бійтеся використовувати наукові позначення

Якщо ви маєте справу з парними або плаваючими, ви можете використовувати наукові позначення для чисел. Тож замість написання double a=1000ви можете змінити його, double a=1e3щоб зберегти 1 байт.


7

Спробуйте використовувати intзамістьboolean

У деяких випадках я виявив, що коротше повернути ціле значення з методу, який, як правило, повертає булеве значення, аналогічно тому, що може бути зроблено в програмах C.

Прямо біля кажана intна 4 байти коротше boolean. Кожен раз, коли ви пишете return 0замість return 1<0, ви зберігаєте додаткові 2 байти і стільки ж для return 1 понад return 1>0.

Проблема тут полягає в тому, що кожен раз, коли ви бажаєте використовувати повернене значення безпосередньо як булеве, воно коштує 2 байти ( if(p(n))v. if(p(n)>0)). Це можна скласти за допомогою булевої арифметики. Дано надуманий сценарій, куди потрібно писати

void a(int[]t){t[0]+=p(n)?10:0;}

ви можете замість цього написати

void a(int[]t){t[0]+=p(n)*10;}

щоб зберегти 2 байти.


6
Я роблю це досить часто , коли гра в гольф, але майте на увазі , що загальний консенсус в тому , що 0і 1не є помилковими / вірно в Java (і JLS не вважає їх таким чином або). Тож якщо гольф спеціально просить тріфу / фальшивку, то вам потрібно розмитнути його (і, на жаль, зробити його booleanфункцією, кидаючи на нього ще більше байтів).
Геобіт

2
t[0]+=p(n):10?0;Це навіть справедливо?
снідакайхан

@dorukayhan ні, це задумано t[0]+=p(n)?10:0;. (Я відредагував це.)
Paŭlo Ebermann

6

Якщо ви використовуєте enum замість класу , ви зберігаєте один символ.

enum NoClass {
    F, G, H;    
    public static void main (String[] args) {

    }
}

Але ви повинні ввести принаймні один екземпляр перерахунку (F, G, H у цьому прикладі), який повинен окупитися.


2
Здається, вам не потрібні жодні екземпляри перерахунків. Я робив enum M{;public static void main(String[]a){...}без проблем.
Денні

3
@Danny Але тоді це не рятує жодних символів. class M{рівно такої ж довжини, як і enum M{;. У такому випадку я б поїхав із тим, classщо він гарніший (IMO)
Джастін,

1
принаймні для мене enum{працював без ;пізніше; єдиний IDE стогне, що є помилка, але компілятор приймає це
masterX244

@ masterX244 Що таке компілятор / версія? Моя кидає істерику і не зробить цього.
Геобіц

працював на java 1.7 для мене (з'явився s, ned для розслідування подальшої причини з оновленням до .8 він перестав працювати)
masterX244

5

Коли у вас є метод, який повинен повернути a booleanабо Boolean, тобто:

// Return true if the (non-negative) input is dividable by 5
boolean c(int i){return i%5<1;}

Ви можете змінити тип boolean/ Booleanreturn, Objectщоб зберегти 1 байт:

Object c(int i){return i%5<1;}

Крім того, як ви могли помітити, ви можете використовувати <1чек замість того, ==0щоб зберегти байт. Хоча це скоріше загальна порада з кодовим гольфом, а не специфічна для Java.
В основному це використовується, коли ціле число не може бути від'ємним, як перевірка на довжину:

a.length<1

замість

a.length==0

1
Гарна порада! Ви можете додати ще один приклад у розділ "якщо він не може бути негативним", щоб проілюструвати його, оскільки c(-21)повертається trueз поточним.
Геобіт

Уточнено. Крім того, ви не маєте на увазі c(-20)замість цього -21? -21 % 5 = 4і -20 % 5 = 0.
Кевін Кройсейсен

1
Ні, я мав на увазі -21. -21 % 5 != 4на Java, що моє значення. Функція, що ділиться на п'ять , спрацювала б правильно, якби модуль завжди повертався негативно, але це не так. Дивіться цей фрагмент прикладу .
Геобіт

@Geobits Ах, дякую за приклад. Я майже ніколи не використовую мінусові числа %, тому я забув, що Java повертає залишки замість модуля, отже, різниця ..
Кевін Круїйссен

5

Як малювати на Java ...

Ось найкоротший можливий графічний котел із фарбою GUI:

import java.awt.*;
static void main(String[]x){
    new Frame(){
        public void paint(Graphics g){
            // Draw your stuff here.
        }    
    }.show();
}

Поле для гольфу на 111 байт:

import java.awt.*;static void main(String[]x){new Frame(){public void paint(Graphics g){/*CodeHere*/}}.show();}

5

Уникайте StringBuilders

Додавання матеріалу до значень Stringзаймає набагато менше байтів.

// s is a StringBuilder
s.append("Hello, World!");

// S is a String
S+="Hello, World!";

Якщо вам доведеться негайно повернути рядок і роздрукувати його, скористайтеся a StringBuffer.

System.out.print(new StringBuilder("Hello, World!").reverse());
System.out.print(new StringBuffer("Hello, World!").reverse()); // Note that you can omit toString() when printing a non-String object

Якщо вам потрібно повернути рядок, а потім зробити щось інше, ніж друкувати його, використовуйте forкожен цикл.

String b=new StringBuffer("Hello, World!").reverse().toString();
String B="";for(String c:"Hello, World!".split(""))B=c+B;

3
Петля переднього плану коротша, ніж StringBufferдля обертання рядків. String b="";for(char c:"Hello, World!".toCharArray()){b=c+b;}
Поп

1
Ви також повинні видалити {}з цього циклу foreach, якщо ви збираєтесь використовувати цей метод.
Геобіт

1
Збережіть 2 байти, використовуючи String s:"".split("")замість char c:"".toCharArray().
Чарлі

Якщо ви java.util.stream.Streamвже імпортували, і якщо вам потрібно зав'язати ще один виклик до результату (наприклад B.chartAt(42)) або якщо вам просто потрібно передати результат функції (на зразок f(B)), то використання for(:)буде дорівнює Stream.of("Hello, World!".split("")).reduce("",(a,b)->b+a).
Чарлі

Обидві лінії у вашому прикладі з кодом для кожного можуть бути гольфними. Першим може стати: String b=new StringBuffer("Hello, World!").reverse()+"";( .toStringзамінено на +""), а другим рядком може стати: String B="";for(String c:"Hello, World!".split(""))B=c+B;( charдо Stringі .toCharArray()до .split("")).
Кевін Круїссен

5

Використовуйте Java 10 var

Якщо ви визначаєте одну змінну конкретного типу, використовуйте var .

Приклади

var i=0;                        // int
var l=0L;                       // long
var s="";                       // String
var a=new int[]{1,2,3};         // int[]
var i=java.math.BigInteger.ONE; // BigInteger
var m=new java.util.HashMap();  // HashMap
var i=3+"abc".length()          // int
var a="a b c".split(" ");       // String[]
for(var a:"a b c".split(" "))   // String

Не використовується в жодному з наступних прикладів

var не може бути використаний у багатьох прикладах

var i=1,j=2;           // only one variable is accepted at a time
var a={1,2,3};         // arrays must be explicitly declared
var f=a->a+" ";        // can't know what type a is.
var f=String::replace; // method references aren't properly implied (weirdly, though)

Чому це не працює з посиланнями на методи, зауважте, що є стандартні функціональні інтерфейси лише для невеликого набору підписів (і методи можуть кидати перевірені винятки).
Якоб


4

У більшості випадків ваша програма буде однопотоковою, тобто вона матиме лише один потік. Ви можете використовувати цей факт, returnвикористовуючи основний метод, коли вам доведеться миттєво вийти.

static void main(String[]a){if(condition)return;}

Порівняйте це з "правильним" завершенням програми:

static void main(String[]a){if(condition)System.exit(0);}

Або вказує на null:

static void main(String[]a){if(condition)throw null;}

Або ділення на 0:

static void main(String[]a){if(condition)int A=1/0;}

4

Іноді один оператор for-циклу може бути заміним. Розглянемо наступний код:

int m(int x){int i=1;for(;x%++i==0;);return i;}

Це простий цикл, який є рішенням цього питання .

Оскільки ми знаємо, що iце не буде досить великим розміром, щоб викликати помилки StackOverflow, ми можемо замінити фор-цикл замість рекурсії:

int m(int x,int i){return x%++i>0?i:m(x,i);}

Ми можемо імітувати цикл, використовуючи потрійний оператор у операторі return, щоб викликати рекурсію.

Це зменшення досить специфічне, але я можу уявити більше ситуацій, коли це стане в нагоді.


4

Використання ... (varags) в якості параметра

У деяких випадках коротше використовувати вараги Java як параметр замість вільних.
Наприклад:

// Example input/output: 5, 4, 3 -> 60000
int calculateVolumeInLiters(int width, int height, int depth){
  return width * height * depth * 1000;
}

До цього найбільше пограли б гольфи:

int c(int w,int h,int d){return w*h*d*1000;} // 44 bytes

Але може бути додатковий байт до цього:

int c(int...a){return a[0]*a[1]*a[2]*1000;}  // 43 bytes

Зауважте, що всі три цілі числа доступні лише один раз у самому методі. Оскільки intце досить коротко, це вигідно, якщо ви використовуєте їх лише один раз всередині методу і маєте три чи більше з них як параметр.

Однак при більш довгих параметрах це, як правило, корисніше. Наприклад, це була моя оригінальна відповідь на це завдання (обчислити значення вхідного символу у вхідному рядку):

// Example input/output: tttggloyoi, t -> 3

int c(String a,char b){return a.replaceAll("[^"+b+"]","").length();} // 68 bytes

І мені рекомендували гольфувати це:

int c(String a,char b){return a.split(b+"").length-1;}               // 54 bytes

Але я закінчила гольф на цьому, використовуючи ...:

int c(String...a){return a[0].split(a[1]).length-1;}                 // 52 bytes

ПРИМІТКА. Якщо питання / виклик вимагає гнучких даних, його ...можна, []звичайно, скоротити . Якщо запитання / виклик спеціально вимагає, скажімо, трьох Stringвходів і заборонити Stringмасив, що містить три значення, ви можете використовувати String...замість String a,String b,String c.


2
Ви не можете використовувати String[]замість варагів? (економить ще 1 байт)
Kritixi Lithos

@KritixiLithos Хм .. хороший момент. Але це головним чином залежить від того, наскільки гнучким є вхід для вирішення проблеми. Якщо дозволений будь-який формат введення, то він справді буде коротшим. Я додам це до цих порад, дякую.
Кевін Круїссен
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.