Эффективность Java & ldquo; инициализация двойной скобки & rdquo ;?

И не забудьте изменить в Index.html следующий код:

 <script src="http://192.168.1.4:8000/socket.io/socket.io.js"></script>
 <script src="http://code.jquery.com/jquery-1.6.2.min.js"></script>

 var socket = io.connect('http://192.168.1.4:8000');

Удачи!

767
задан Jim Ferrans 10 December 2017 в 23:02
поделиться

9 ответов

Помимо эффективности, я редко замечаю, что хочу создать декларативную коллекцию вне модульных тестов. Я верю, что синтаксис двойной скобки очень удобен для чтения.

Другой способ добиться декларативного построения списков - использовать Arrays.asList (T ...) следующим образом:

List<String> aList = Arrays.asList("vanilla", "strawberry", "chocolate");

Ограничением этого подхода является, конечно, то, что вы не можете контролировать конкретный тип создаваемого списка.

Не говоря уже об увеличении необходимого дискового пространства для хранения всех этих файлов класса .

Похоже, что при использовании инициализации с двойными скобками возникают небольшие накладные расходы, поэтому, вероятно, это не так хорошая идея переборщить с этим. Но, как заметил Эдди в комментариях, невозможно быть абсолютно уверенным в воздействии.


Для справки, инициализация двойной скобки выглядит следующим образом:

List<String> list = new ArrayList<String>() {{
    add("Hello");
    add("World!");
}};

Это похоже на «скрытую» функцию Java, но она это просто переписывание:

List<String> list = new ArrayList<String>() {

    // Instance initialization block
    {
        add("Hello");
        add("World!");
    }
};

Таким образом, это в основном блок инициализации экземпляра , который является частью анонимного внутреннего класса .


Предложение Джошуа Блоха Collection Literals ] для Project Coin был примерно таким:

List<Integer> intList = [1, 2, 3, 4];

Set<String> strSet = {"Apple", "Banana", "Cactus"};

Map<String, Integer> truthMap = { "answer" : 42 };

К сожалению, это не currentTimeMillis , поэтому таймер не имеет очень высокого разрешения. В моей системе Windows разрешение составляет около 15-16 миллисекунд.

Результаты для 10 запусков двух тестов были следующими:

Test1 Times (ms)           Test2 Times (ms)
----------------           ----------------
           187                          0
           203                          0
           203                          0
           188                          0
           188                          0
           187                          0
           203                          0
           188                          0
           188                          0
           203                          0

Как можно видеть, инициализация двойной скобки имеет заметное время выполнения около 190 мс.

Между тем, время выполнения инициализации ArrayList оказалось равным 0 мс. Конечно, необходимо учитывать разрешение таймера, но оно, скорее всего, будет меньше 15 мс.

Таким образом, кажется, есть заметная разница во времени выполнения двух методов. Похоже, что действительно есть некоторые накладные расходы в двух методах инициализации.

И да, было 1000 .class файлов, сгенерированных компиляцией тестовой программы инициализации двойных скобок Test1 .

поэтому таймер не имеет очень высокого разрешения. В моей системе Windows разрешение составляет около 15-16 миллисекунд.

Результаты для 10 запусков двух тестов были следующими:

Test1 Times (ms)           Test2 Times (ms)
----------------           ----------------
           187                          0
           203                          0
           203                          0
           188                          0
           188                          0
           187                          0
           203                          0
           188                          0
           188                          0
           203                          0

Как можно видеть, инициализация двойной скобки имеет заметное время выполнения около 190 мс.

Между тем, время выполнения инициализации ArrayList оказалось равным 0 мс. Конечно, необходимо учитывать разрешение таймера, но оно, скорее всего, будет меньше 15 мс.

Таким образом, кажется, есть заметная разница во времени выполнения двух методов. Похоже, что действительно есть некоторые накладные расходы в двух методах инициализации.

И да, было 1000 .class файлов, сгенерированных компиляцией тестовой программы инициализации двойных скобок Test1 .

поэтому таймер не имеет очень высокого разрешения. В моей системе Windows разрешение составляет около 15-16 миллисекунд.

Результаты для 10 запусков двух тестов были следующими:

Test1 Times (ms)           Test2 Times (ms)
----------------           ----------------
           187                          0
           203                          0
           203                          0
           188                          0
           188                          0
           187                          0
           203                          0
           188                          0
           188                          0
           203                          0

Как можно видеть, инициализация двойной скобки имеет заметное время выполнения около 190 мс.

Между тем, время выполнения инициализации ArrayList оказалось равным 0 мс. Конечно, необходимо учитывать разрешение таймера, но оно, скорее всего, будет меньше 15 мс.

Таким образом, кажется, есть заметная разница во времени выполнения двух методов. Похоже, что действительно есть некоторые накладные расходы в двух методах инициализации.

И да, было 1000 .class файлов, сгенерированных компиляцией тестовой программы инициализации двойных скобок Test1 .

разрешение составляет около 15-16 миллисекунд.

Результаты для 10 запусков двух тестов были следующими:

Test1 Times (ms)           Test2 Times (ms)
----------------           ----------------
           187                          0
           203                          0
           203                          0
           188                          0
           188                          0
           187                          0
           203                          0
           188                          0
           188                          0
           203                          0

Как можно видеть, инициализация двойной скобки имеет заметное время выполнения около 190 мс.

Между тем, время выполнения инициализации ArrayList оказалось равным 0 мс. Конечно, необходимо учитывать разрешение таймера, но оно, скорее всего, будет меньше 15 мс.

Таким образом, кажется, есть заметная разница во времени выполнения двух методов. Похоже, что действительно есть некоторые накладные расходы в двух методах инициализации.

И да, было 1000 .class файлов, сгенерированных компиляцией тестовой программы инициализации двойных скобок Test1 .

разрешение составляет около 15-16 миллисекунд.

Результаты для 10 запусков двух тестов были следующими:

Test1 Times (ms)           Test2 Times (ms)
----------------           ----------------
           187                          0
           203                          0
           203                          0
           188                          0
           188                          0
           187                          0
           203                          0
           188                          0
           188                          0
           203                          0

Как можно видеть, инициализация двойной скобки имеет заметное время выполнения около 190 мс.

Между тем, время выполнения инициализации ArrayList оказалось равным 0 мс. Конечно, необходимо учитывать разрешение таймера, но оно, скорее всего, будет меньше 15 мс.

Таким образом, кажется, есть заметная разница во времени выполнения двух методов. Похоже, что действительно есть некоторые накладные расходы в двух методах инициализации.

И да, было 1000 .class файлов, сгенерированных компиляцией тестовой программы инициализации двойных скобок Test1 .

Test1 Times (ms)           Test2 Times (ms)
----------------           ----------------
           187                          0
           203                          0
           203                          0
           188                          0
           188                          0
           187                          0
           203                          0
           188                          0
           188                          0
           203                          0

Как видно, время выполнения инициализации двойной скобки составляет около 190 мс.

Между тем время выполнения инициализации ArrayList оказалось равным 0 мс. Конечно, необходимо учитывать разрешение таймера, но оно, скорее всего, будет меньше 15 мс.

Таким образом, кажется, есть заметная разница во времени выполнения двух методов. Похоже, что действительно есть некоторые накладные расходы в двух методах инициализации.

И да, было 1000 .class файлов, сгенерированных компиляцией тестовой программы инициализации двойных скобок Test1 .

Test1 Times (ms)           Test2 Times (ms)
----------------           ----------------
           187                          0
           203                          0
           203                          0
           188                          0
           188                          0
           187                          0
           203                          0
           188                          0
           188                          0
           203                          0

Как видно, время выполнения инициализации двойной скобки составляет около 190 мс.

Между тем время выполнения инициализации ArrayList оказалось равным 0 мс. Конечно, необходимо учитывать разрешение таймера, но оно, скорее всего, будет меньше 15 мс.

Таким образом, кажется, есть заметная разница во времени выполнения двух методов. Похоже, что действительно есть некоторые накладные расходы в двух методах инициализации.

И да, было 1000 .class файлов, сгенерированных компиляцией тестовой программы инициализации двойных скобок Test1 .

разрешение таймера должно быть принято во внимание, но оно, вероятно, будет меньше 15 мс.

Таким образом, кажется, есть заметная разница во времени выполнения двух методов. Похоже, что действительно есть некоторые накладные расходы в двух методах инициализации.

И да, было 1000 .class файлов, сгенерированных компиляцией тестовой программы инициализации двойных скобок Test1 .

разрешение таймера должно быть принято во внимание, но оно, вероятно, будет меньше 15 мс.

Таким образом, кажется, есть заметная разница во времени выполнения двух методов. Похоже, что действительно есть некоторые накладные расходы в двух методах инициализации.

И да, было 1000 .class файлов, сгенерированных компиляцией тестовой программы инициализации двойных скобок Test1 .

589
ответ дан 22 November 2019 в 21:21
поделиться
  1. Это вызовет add () для каждого члена. Если вы можете найти более эффективный способ поместить элементы в хэш-набор, воспользуйтесь им. Обратите внимание, что внутренний класс, скорее всего, будет генерировать мусор, если вы к этому относитесь осторожно.

  2. Мне кажется, что контекст - это объект, возвращаемый new , то есть HashSet .

  3. Если вам нужно спросить ... Скорее: люди, которые придут после вас, узнают об этом или нет? Легко понять и объяснить? Если вы можете ответить «да» на оба вопроса, смело используйте его.

2
ответ дан 22 November 2019 в 21:21
поделиться

Я второй ответ Nat, за исключением того, что я бы использовал цикл вместо создания и немедленного отбрасывания неявного списка из asList (элементы):

static public Set<T> setOf(T ... elements) {
    Set set=new HashSet<T>(elements.size());
    for(T elm: elements) { set.add(elm); }
    return set;
    }
3
ответ дан 22 November 2019 в 21:21
поделиться

Там ' Как правило, в этом нет ничего особенно неэффективного. Для JVM обычно не имеет значения, что вы создали подкласс и добавили к нему конструктор - это обычное повседневное дело в объектно-ориентированном языке. Я могу думать о довольно надуманных случаях, когда вы могли бы вызвать неэффективность, делая это (например, у вас есть многократно вызываемый метод, который в конечном итоге принимает смесь разных классов из-за этого подкласса, тогда как обычно переданный класс будет полностью предсказуемым - - в последнем случае JIT-компилятор может произвести оптимизацию, которая невозможна в первом случае). Но на самом деле, я думаю, что случаи, когда это будет иметь значение, очень надуманы.

Я бы рассмотрел проблему больше с точки зрения того, хотите ли вы «загромождать вещи» множеством анонимных классов. В качестве приблизительного руководства рассмотрите возможность использования идиомы не больше, чем вы использовали бы, скажем, анонимные классы для обработчиков событий.

В (2) вы находитесь внутри конструктора объекта, поэтому «this» относится к объекту, который вы создаете . Это ничем не отличается от любого другого конструктора.

Что касается (3), я думаю, это действительно зависит от того, кто поддерживает ваш код. Если вы не знаете этого заранее, тогда я бы предложил использовать тест «вы видите это в исходном коде JDK?» (в этом случае я не припомню, чтобы встречал много анонимных инициализаторов, и уж точно не в тех случаях, когда это только содержимое анонимного класса). Я бы сказал, что в большинстве проектов среднего размера вам действительно понадобятся ваши программисты, чтобы понять исходный код JDK в какой-то момент, так что любой используемый здесь синтаксис или идиома считается «честной игрой». Помимо этого, я бы сказал, обучите людей этому синтаксису, если вы контролируете, кто поддерживает код, иначе комментируйте или избегайте.

7
ответ дан 22 November 2019 в 21:21
поделиться

Для создания наборов вы можете использовать фабричный метод varargs вместо инициализации двойных скобок:

public static Set<T> setOf(T ... elements) {
    return new HashSet<T>(Arrays.asList(elements));
}

В библиотеке Google Collections есть множество таких удобных методов, а также множество других полезных функций.

Что касается неясности идиомы, я постоянно сталкиваюсь с ней и использую ее в производственном коде. Меня больше беспокоят программисты, которых смущает идиома, которой разрешено писать производственный код.

16
ответ дан 22 November 2019 в 21:21
поделиться

Взяв следующий тестовый класс:

public class Test {
  public void test() {
    Set<String> flavors = new HashSet<String>() {{
        add("vanilla");
        add("strawberry");
        add("chocolate");
        add("butter pecan");
    }};
  }
}

и затем декомпилируя файл класса, я вижу:

public class Test {
  public void test() {
    java.util.Set flavors = new HashSet() {

      final Test this$0;

      {
        this$0 = Test.this;
        super();
        add("vanilla");
        add("strawberry");
        add("chocolate");
        add("butter pecan");
      }
    };
  }
}

Мне это не кажется ужасно неэффективным. Если бы я беспокоился о производительности для чего-то вроде этого, я бы профилировал это. И на ваш вопрос №2 отвечает приведенный выше код: вы находитесь внутри неявного конструктора (и инициализатора экземпляра) для вашего внутреннего класса, поэтому « this » относится к этому внутреннему классу.

Да, этот синтаксис неясен, но комментарий может прояснить использование неясного синтаксиса. Чтобы прояснить синтаксис, большинство людей знакомы со статическим блоком инициализатора (JLS 8.7 Static Initializers):

public class Sample1 {
    private static final String someVar;
    static {
        String temp = null;
        ..... // block of code setting temp
        someVar = temp;
    }
}

Вы также можете использовать аналогичный синтаксис (без слова « static ») для использования конструктора (JLS 8.6 Инициализаторы экземпляра), хотя я никогда не видел, чтобы это использовалось в производственном коде. Это гораздо менее известно.

public class Sample2 {
    private final String someVar;

    // This is an instance initializer
    {
        String temp = null;
        ..... // block of code setting temp
        someVar = temp;
    }
}

Если у вас нет конструктора по умолчанию, то блок кода между { и } превращается компилятором в конструктор. Имея это в виду, распутайте код двойной скобки:

public void test() {
  Set<String> flavors = new HashSet<String>() {
      {
        add("vanilla");
        add("strawberry");
        add("chocolate");
        add("butter pecan");
      }
  };
}

Блок кода между самыми внутренними скобками превращается компилятором в конструктор. Самые внешние фигурные скобки ограничивают анонимный внутренний класс. Сделаем последний шаг к тому, чтобы сделать все неанонимным:

public void test() {
  Set<String> flavors = new MyHashSet();
}

class MyHashSet extends HashSet<String>() {
    public MyHashSet() {
        add("vanilla");
        add("strawberry");
        add("chocolate");
        add("butter pecan");
    }
}

В целях инициализации я бы сказал, что нет никаких накладных расходов (или настолько малых, что ими можно пренебречь). Однако каждое использование разновидностей будет идти не против HashSet , а вместо MyHashSet . Вероятно, это связано с небольшими (и, возможно, незначительными) накладными расходами. Но опять же, прежде чем я переживал об этом,

35
ответ дан 22 November 2019 в 21:21
поделиться

One property of this approach that has not been pointed out so far is that because you create inner classes, the whole containing class is captured in its scope. This means that as long as your Set is alive, it will retain a pointer to the containing instance (this$0) and keep that from being garbage-collected, which could be an issue.

This, and the fact that a new class gets created in the first place even though a regular HashSet would work just fine (or even better), makes me not want to use this construct (even though I really long for the syntactic sugar).

Second question: The new HashSet must be the "this" used in the instance initializer ... can anyone shed light on the mechanism? I'd have naively expected "this" to refer to the object initializing "flavors".

This is just how inner classes work. They get their own this, but they also have pointers to the parent instance, so that you can call methods on the containing object as well. In case of a naming conflict, the inner class (in your case HashSet) takes precedence, but you can prefix "this" with a classname to get the outer method as well.

public class Test {

    public void add(Object o) {
    }

    public Set<String> makeSet() {
        return new HashSet<String>() {
            {
              add("hello"); // HashSet
              Test.this.add("hello"); // outer instance 
            }
        };
    }
}

To be clear on the anonymous subclass being created, you could define methods in there as well. For example override HashSet.add()

    public Set<String> makeSet() {
        return new HashSet<String>() {
            {
              add("hello"); // not HashSet anymore ...
            }

            @Override
            boolean add(String s){

            }

        };
    }
102
ответ дан 22 November 2019 в 21:21
поделиться

Efficiency aside, I rarely find myself wishing for declarative collection creation outside of unit tests. I do believe that the double brace syntax is very readable.

Another way to achieve the declarative construction of lists specifically is to use Arrays.asList(T ...) like so:

List<String> aList = Arrays.asList("vanilla", "strawberry", "chocolate");

The limitation of this approach is of course that you cannot control the specific type of list to be generated.

9
ответ дан 22 November 2019 в 21:21
поделиться

Марио Глейхман описывает , как использовать общие функции Java 1.5 для имитации литералов списка Scala, хотя, к сожалению, вы получаете неизменяемые списки.

Он определяет этот класс:

package literal;

public class collection {
    public static <T> List<T> List(T...elems){
        return Arrays.asList( elems );
    }
}

и использует его следующим образом:

import static literal.collection.List;
import static system.io.*;

public class CollectionDemo {
    public void demoList(){
        List<String> slist = List( "a", "b", "c" );
        List<Integer> iList = List( 1, 2, 3 );
        for( String elem : List( "a", "java", "list" ) )
            System.out.println( elem );
    }
}

Google Collections, теперь часть Guava , поддерживает аналогичную идею построения списков. В интервью Джаред Леви говорит:

[...] наиболее часто используемые функции, которые появляются почти в каждом классе Java, который я пишу, - это статические методы, которые уменьшают количество повторяющихся нажатий клавиш в вашем коде Java. Так удобно вводить следующие команды:

Map = Maps.newHashMap ();

List animals = Lists.immutableList ("кошка", "собака", " лошадь ");

10.07.2014: Если бы это могло быть так же просто, как у Питона:

animals = ['кошка', 'собака', 'лошадь']

3
ответ дан 22 November 2019 в 21:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: