Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Почему списки билдера инициализируются ArrayList а не LinkedList?
нужен пруф и тесты
There are two general-purpose List implementations — ArrayList and LinkedList. Most of the time, you'll probably use ArrayList, which offers constant-time positional access and is just plain fast. It does not have to allocate a node object for each element in the List, and it can take advantage of System.arraycopy when it has to move multiple elements at the same time. Think of ArrayList as Vector without the synchronization overhead.
If you frequently add elements to the beginning of the List or iterate over the List to delete elements from its interior, you should consider using LinkedList. These operations require constant-time in a LinkedList and linear-time in an ArrayList. But you pay a big price in performance. Positional access requires linear-time in a LinkedList and constant-time in an ArrayList. Furthermore, the constant factor for LinkedList is much worse. If you think you want to use a LinkedList, measure the performance of your application with both LinkedList and ArrayList before making your choice; ArrayList is usually faster.
Когда я оптимизировал работу с BigData мне приходилось отказываться не только от LinkedList, но даже от HashMap и ArrayList и работать тупо с массивами.
ArrayList же просто обёртка над массивом. Что в нём было неэффективно и возможна ли другая более эффективная обёртка над массивом?
И чем вы заменили HashMap? Параллельными сортированными массивами? И возможна ли эффективная обёртка?
public class HelloWorld{
static class A {
int a=0;
}
public static void main(String []args){
List<A> as = new ArrayList<>();
for(int i=0; i<10; ++i)
as.add(new A(){{a=i;}});
}
}
for(final int i : new int[] {1,2,3}) {
new MyClass.Builder(){{ first = i; third = "3"; }}.create();
}
new Foo(1) { Bar = 2, Baz = 3 }В отличие от Java, отдельный класс для каждого случая генерироваться не будет, никакой контекст захватываться тоже не будет, это просто запись свойств.new Foo(foo: 1, bar: 2, baz: 3)
Foo.CreateFromBar(2, baz: 3) Этот способ даёт возможность пропускать имена аргументов, но можно прописать в конвенции (а также прикрутить проверку в IDE и при коммите по вкусу), что в таких случаях использование именованных аргументов обязательно.new Foo { Bars = { 1, 2, 3 }, Bazs = { [1] = 2, [3] = 4 } } Кажется, обсуждали поддержку блока инициализации ещё и для статических методов, но не помню, к чему там пришли.По поводу варианта А — в этом случае наверное не может быть final полей и не могут выполнятся проверки.
Вы же согласны что лучше иметь сложные проверки отдельно от конструктора, в билдере.
Тогда возникает вопрос, а как будет работать наследование?
using static System.Console;
class CsBaseClass
{
public int First { get; }
public double Second { get; }
public string Third { get; }
public CsBaseClass (int first = -1, double second = double.NaN, string third = null)
{
First = first;
Second = second;
Third = third;
}
public override string ToString () => $"{GetType().Name} - First: {First}, Second: {Second}, Third: {Third}";
}
class CsChildClass : CsBaseClass
{
public object Fourth { get; }
public CsChildClass (int first = -1, double second = double.NaN, string third = null, object fourth = null)
: base(first, second, third)
{
Fourth = fourth;
}
public override string ToString () => $"{base.ToString()}, Fourth: {Fourth}";
}
class Program
{
static void Main ()
{
WriteLine(new CsBaseClass(first: 1, third: "3"));
WriteLine(new CsChildClass(first: 1, third: "3", fourth: 4));
}
}using static System.Console;
class CsBaseClass (int First = -1, double Second = double.NaN, string Third = null);
class CsChildClass : CsBaseClass (int First = -1, double Second = double.NaN, string Third = null, object Fourth = null);
class Program
{
static void Main ()
{
WriteLine(new CsBaseClass(First: 1, Third: "3"));
WriteLine(new CsChildClass(First: 1, Third: "3", Fourth: 4));
}
}PS. А инициализация коллекций и словарей многоуровневая или только первый уровень поддерживается?
public class LocalBuilderExample {
protected final double base;
public LocalBuilderExample(double base) {
super();
this.base = base;
}
public class BaseBuilder extends NewBaseClass.BuilderImpl {
public NewBaseClass create() {
if(!Double.isNaN(base)) {
first = (int) Math.round(base) + first;
if(!Double.isNaN(base)) {
second = base + second;
} else {
second = base;
}
}
return new NewBaseClass(this);
};
}
}
Скажем, вот код, эквивалентный вашим NewBaseClass.java и NewChildClass.java (где-то в два раза короче):
C# Java
class BaseClass { = class BaseClass {
+ static class BuilderImpl {
+ int first = -1 ;
+ double second = Double.NaN;
+ String third = null ;
+ }
+5
+ static class Builder extends BuilderImpl {
+ BaseClass create() { return new BaseClass(this); }
+ }
+3
const int DEFAULT_First = -1 ; +
const double DEFAULT_Second = double.NaN; +
const string DEFAULT_Third = null ; +
+3
const int First { get; } = final int first ;
const double Second { get; } = final double second;
const string Third { get; } = final String third ;
+ int first () { return first ; }
+ double second() { return second; }
+ String third () { return third ; }
+3
BaseClass( = BaseClass(
int first = DEFAULT_First , = BuilderImpl builder
double second = DEFAULT_Second, +
string third = DEFAULT_Third , +
) { +2= ) {
First = first; = this.first = builder.first ;
Second = second; = this.second= builder.second;
Third = third; = this.third = builder.third ;
} = }
} = }
=12
+5 +11
+6
17 23
class ChildClass : BaseClass { = class ChildClass extends BaseClass {
+ static class BuilderImpl extends BaseClass.BuilderImpl {
+ Object fourth = null;
+3 }
+ static class Builder extends BuilderImpl {
+ ChildClass create() { return new ChildClass(this); }
+3 }
const object DEFAULT_Fourth = null; +
+1
const object Fourth { get; } = final Object fourth;
+ Object fourth() { return fourth; }
+1
ChildClass ( = ChildClass(
int first = DEFAULT_First , = BuilderImpl builder
double second = DEFAULT_Second, +
string third = DEFAULT_Third , +
object fourth = DEFAULT_Fourth +
) : base( +3= ) { super(
first , = builder
second, +
third +
) { +2= );
Fourth = fourth; = this.fourth = builder.fourth;
} = }
} = }
=10
+6 +7
+1
16 17
=22
+11 +18
+7
33 40
В C# 7 планируется сделать что-то вроде этого
Многоуровневая.
Вы предлагаете в таком случае дублировать все описания параметров вместе со всеми дефолтными значениями.
В вашем коде не хватает констант для дефолтных значений — это соответственно увеличит код.
Добавим их и сравним построчно код игнорируя пустые строки (я убрал модификаторы доступа чтобы влезло)
ChildClass (
int first = DEFAULT_First ,
double second = DEFAULT_Second,
string third = DEFAULT_Third ,
object fourth = DEFAULT_Fourth
) : base(
first ,
second,
third
) {
Fourth = fourth;
}HWND hwnd = CreateWindowEx(
0,
CLASS_NAME,
L"Learn to Program Windows",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);"\s+" => " "). Мерять только строчками некорректно, потому что сложность восприятия строчек разной длины разная.using static System.Console;
public class CsBaseClass
{
public const int DefaultFirst = -1;
public const double DefaultSecond = double.NaN;
public const string DefaultThird = null;
public int First { get; }
public double Second { get; }
public string Third { get; }
public CsBaseClass (int first = DefaultFirst, double second = DefaultSecond, string third = DefaultThird)
{
First = first;
Second = second;
Third = third;
}
public override string ToString () => $"{GetType().Name} - First: {First}, Second: {Second}, Third: {Third}";
}
public class CsChildClass : CsBaseClass
{
public const object DefaultFourth = null;
public object Fourth { get; }
public CsChildClass (int first = DefaultFirst, double second = DefaultSecond, string third = DefaultThird,
object fourth = DefaultFourth)
: base(first, second, third)
{
Fourth = fourth;
}
public override string ToString () => $"{base.ToString()}, Fourth: {Fourth}";
static void Main ()
{
WriteLine(new CsBaseClass(first: 1, third: "3"));
WriteLine(new CsChildClass(first: 1, third: "3", fourth: 4));
}
}public class NewBaseClass {
protected static class BuilderImpl {
public int first = -1 ;
public double second = Double.NaN;
public String third = null ;
}
public static class Builder extends BuilderImpl {
public NewBaseClass create() { return new NewBaseClass(this); }
}
protected final int first ;
protected final double second;
protected final String third ;
protected NewBaseClass(BuilderImpl builder) {
super();
this.first = builder.first ;
this.second= builder.second;
this.third = builder.third ;
}
public int first () { return first ; }
public double second() { return second; }
public String third () { return third ; }
@Override public String toString() {
return "MyClass [first=" + first + ", second=" + second + ", third=" + third + "]";
}
}
public class NewChildClass extends NewBaseClass {
protected static class BuilderImpl extends NewBaseClass.BuilderImpl {
public Object fourth = null;
}
public static class Builder extends BuilderImpl {
public NewChildClass create() { return new NewChildClass(this); }
}
protected final Object fourth;
protected NewChildClass(BuilderImpl builder) {
super(builder);
this.fourth = builder.fourth;
}
public Object fourth() { return fourth; }
@Override public String toString() {
return "MyChildClass [first=" + first + ", second=" + second + ", third=" + third + ", fourth=" + fourth + "]";
}
public static void main(String[] args) {
System.out.println(new NewBaseClass.Builder(){{ first = 1; third = "3"; }}.create());
System.out.println(new NewChildClass.Builder(){{ first = 1; third = "3"; fourth = 4; }}.create());
}
}Ну конечно в варианте с большим количеством параметров гораздо легче ошибиться (несоответствие типа, несоответствие порядка)
Вы предлагаете в таком случае дублировать все описания параметров вместе со всеми дефолтными значениями.
Что-то мешает вынести в отдельную функцию?
Как я уже сказал, этот код вручную писать необязательно. «Написание» конструктора для класса-потомка у меня свелось к аккорду
Создание новой функции приведёт к дублированию всего описания параметров. А в случае билдера — нет.
Выводы: В худшем синтетическом случае кода больше на 40% и то это сокращение в основном из-за getter-ов. В реальном коде увеличение кода даже в таком минимальном варианте (только два класса) не будет более чем на треть.
Что будет в случае следующих рефакторингов?
В случае выделенного объекта билдер это простые контролируемые компилятором операции.
Вообще-то дублирование параметров — неизбежное зло. Вам никогда не приходилось прокидывать аргументы через несколько вызовов по цепочке?
Вы так говорите про дублирование, как будто слово «first» повторяется в джавовом коде реже, чем в шарповом.
Хоть как-то приближается к избавлению от дублирования только планируемый синтаксис C# 7
Для меня 30–50% — это очень даже ощутимое число. И, как я уже сказал, различия в количестве классов даже более важны, чем слоки, байты и непробельные символы.
Что будет в случае следующих рефакторингов?
Ну, будет работа не на часик, а на два. Вроде, даже решарпер не очень поможет, потому что полноценная обработка цепочек конструкторов не реализована. Фиче-реквест был, но, вроде, до него ещё не добрались.
В случае выделенного объекта билдер это простые контролируемые компилятором операции.
Я ж уже говорил: это весьма эфемерный бонус. Тип и имя поля-то вы измените, даже сэкономите 50% времени относительно грязной версии с копипастой, но ведь всё равно основное время будет убито на поиск и исправление всех использований во всём коде. Все перечисленные вами изменения ломают обратную совместимость, не получится изменить только одно место.
Нет никакого неизбежного зла — при необходимости часть или весь список параметров превращается в один объект.
Зависит от того насколько то или иное удобно.
Слово first встречается только при певром упоминании — в первом объекте и билдере.
Количество классов само по себе ничему не вредит
Вы будете ради двух-трёх аргументов заводить отдельный класс ради использования один раз?
Это усложняет восприятие кода.
Откройте файл, нажмите Ctrl+F, введите «first» и посчитайте. Вот это всё — дублирование.
Только мы обсуждаем сейчас не автоматическую генерацию геттеров, а именованые параметры vs объекты.
1: public BaseClass(int first = -1...
2: First =
3: first;
против1: public int first = -1
2: protected int first;
3: first =
4: src.first;
1: public ChildClass(int first = -1...
2: base(first...
против0:
Это всё равно что оптимизировать в 10 раз скорость выполнения кода, который отнимает меньше 1% времени.
Можно пример?
class Foo
{
public Foo Foo { get; set; }
public List<Foo> FooList { get; } = new List<Foo>();
public Dictionary<Foo, Foo> FooDic { get; } = new Dictionary<Foo, Foo>();
static void Main ()
{
new Foo {
Foo = new Foo {
FooList = {
new Foo(),
new Foo()
},
FooDic = {
[new Foo()] = new Foo(),
[new Foo()] = new Foo()
},
Foo = new Foo {
FooList = {
new Foo {
FooList = {
new Foo {
FooList = {
new Foo()
}
}
}
}
},
FooDic = {
[new Foo {
Foo = new Foo()
}] = new Foo {
Foo = new Foo()
},
[new Foo {
FooDic = {
[new Foo()] = new Foo()
}
}] = new Foo {
FooDic = {
[new Foo()] = new Foo()
}
}
}
}
}
};
}
}new Foo { Foo = { Foo = new Foo() } } тоже будет работать, если свойство не будет null после вызова конструктора (но конкретно в этом случае будет stack overflow).А как инициализировать List<List> или Dictionary<Foo, List>?
using System.Collections.Generic;
class Foo
{
static void Main ()
{
var foos = new List<Dictionary<List<Dictionary<Foo, Foo>>, List<List<Foo>>>> {
new Dictionary<List<Dictionary<Foo, Foo>>, List<List<Foo>>> {
[new List<Dictionary<Foo, Foo>> {
new Dictionary<Foo, Foo> {
[new Foo()] = new Foo()
}
}] = new List<List<Foo>> {
new List<Foo> {
new Foo()
}
}
}
};
}
}var foos = new List<Dictionary<List<Dictionary<Foo, Foo>>, List<List<Foo>>>> {
[
[
[[new Foo() = new Foo()], [new Foo() = new Foo()]] = [[new Foo(), new Foo()], [new Foo(), new Foo()]],
[[], [new Foo() = new Foo()]] = [[], []]
],
[
[] = [[new Foo(), new Foo()], []],
];
]
};
Result<...> query = create.select(BOOK.AUTHOR_ID, BOOK.TITLE)
.from(BOOK)
.groupBy(AUTHOR_ID)
.fetch();
List<? extends BaseEntity> entities;
public void addEntity(BaseEntity.Builder builder) {
entities.add(builder.build());
}
ExtendedEntity.newBuilder().baseParameter(value).extendedParameter(value2).build();
>> Для использовании наследования, Builder разделяется на две части (один с полями, другой — с методом создания) следующим образом:
Так делать не стоит, сейчас объясню почему
Самый простой и самый сложный Builder на Java