В общем, подход, предложенный CaseyB , отлично работает, на самом деле, если вы проходите в List
, трудно его винить, возможно, я бы изменил его на:
public static IEnumerable> ChunkTrivialBetter(this IEnumerable source, int chunksize)
{
var pos = 0;
while (source.Skip(pos).Any())
{
yield return source.Skip(pos).Take(chunksize);
pos += chunksize;
}
}
Это позволит избежать массовых цепочек вызовов. Тем не менее, этот подход имеет общий недостаток. Он материализует два перечисления на кусок, чтобы подчеркнуть проблему:
foreach (var item in Enumerable.Range(1, int.MaxValue).Chunk(8).Skip(100000).First())
{
Console.WriteLine(item);
}
// wait forever
Чтобы преодолеть это, мы можем попробовать подход Кэмерона , который проходит вышеупомянутый тест в летающих цветах, поскольку он только перебирает перечисление один раз.
Проблема в том, что у него есть другой недостаток, он материализует каждый элемент в каждом фрагменте, проблема с этим подходом заключается в том, что вы работаете высоко в памяти.
Чтобы проиллюстрировать эту попытку:
foreach (var item in Enumerable.Range(1, int.MaxValue)
.Select(x => x + new string('x', 100000))
.Clump(10000).Skip(100).First())
{
Console.Write('.');
}
// OutOfMemoryException
Наконец, любая реализация должна иметь возможность обрабатывать итерацию кусков, например:
Enumerable.Range(1,3).Chunk(2).Reverse().ToArray()
// should return [3],[1,2]
Многие очень оптимальные решения, такие как моя первая версия этого ответа, не удалось. Такую же проблему можно найти в answer.One оптимизированный ответ .
Чтобы решить все эти проблемы, вы можете использовать следующее:
namespace ChunkedEnumerator
{
public static class Extensions
{
class ChunkedEnumerable : IEnumerable
{
class ChildEnumerator : IEnumerator
{
ChunkedEnumerable parent;
int position;
bool done = false;
T current;
public ChildEnumerator(ChunkedEnumerable parent)
{
this.parent = parent;
position = -1;
parent.wrapper.AddRef();
}
public T Current
{
get
{
if (position == -1 || done)
{
throw new InvalidOperationException();
}
return current;
}
}
public void Dispose()
{
if (!done)
{
done = true;
parent.wrapper.RemoveRef();
}
}
object System.Collections.IEnumerator.Current
{
get { return Current; }
}
public bool MoveNext()
{
position++;
if (position + 1 > parent.chunkSize)
{
done = true;
}
if (!done)
{
done = !parent.wrapper.Get(position + parent.start, out current);
}
return !done;
}
public void Reset()
{
// per http://msdn.microsoft.com/en-us/library/system.collections.ienumerator.reset.aspx
throw new NotSupportedException();
}
}
EnumeratorWrapper wrapper;
int chunkSize;
int start;
public ChunkedEnumerable(EnumeratorWrapper wrapper, int chunkSize, int start)
{
this.wrapper = wrapper;
this.chunkSize = chunkSize;
this.start = start;
}
public IEnumerator GetEnumerator()
{
return new ChildEnumerator(this);
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
class EnumeratorWrapper
{
public EnumeratorWrapper (IEnumerable source)
{
SourceEumerable = source;
}
IEnumerable SourceEumerable {get; set;}
Enumeration currentEnumeration;
class Enumeration
{
public IEnumerator Source { get; set; }
public int Position { get; set; }
public bool AtEnd { get; set; }
}
public bool Get(int pos, out T item)
{
if (currentEnumeration != null && currentEnumeration.Position > pos)
{
currentEnumeration.Source.Dispose();
currentEnumeration = null;
}
if (currentEnumeration == null)
{
currentEnumeration = new Enumeration { Position = -1, Source = SourceEumerable.GetEnumerator(), AtEnd = false };
}
item = default(T);
if (currentEnumeration.AtEnd)
{
return false;
}
while(currentEnumeration.Position < pos)
{
currentEnumeration.AtEnd = !currentEnumeration.Source.MoveNext();
currentEnumeration.Position++;
if (currentEnumeration.AtEnd)
{
return false;
}
}
item = currentEnumeration.Source.Current;
return true;
}
int refs = 0;
// needed for dispose semantics
public void AddRef()
{
refs++;
}
public void RemoveRef()
{
refs--;
if (refs == 0 && currentEnumeration != null)
{
var copy = currentEnumeration;
currentEnumeration = null;
copy.Source.Dispose();
}
}
}
public static IEnumerable> Chunk(this IEnumerable source, int chunksize)
{
if (chunksize < 1) throw new InvalidOperationException();
var wrapper = new EnumeratorWrapper(source);
int currentPos = 0;
T ignore;
try
{
wrapper.AddRef();
while (wrapper.Get(currentPos, out ignore))
{
yield return new ChunkedEnumerable(wrapper, chunksize, currentPos);
currentPos += chunksize;
}
}
finally
{
wrapper.RemoveRef();
}
}
}
class Program
{
static void Main(string[] args)
{
int i = 10;
foreach (var group in Enumerable.Range(1, int.MaxValue).Skip(10000000).Chunk(3))
{
foreach (var n in group)
{
Console.Write(n);
Console.Write(" ");
}
Console.WriteLine();
if (i-- == 0) break;
}
var stuffs = Enumerable.Range(1, 10).Chunk(2).ToArray();
foreach (var idx in new [] {3,2,1})
{
Console.Write("idx " + idx + " ");
foreach (var n in stuffs[idx])
{
Console.Write(n);
Console.Write(" ");
}
Console.WriteLine();
}
/*
10000001 10000002 10000003
10000004 10000005 10000006
10000007 10000008 10000009
10000010 10000011 10000012
10000013 10000014 10000015
10000016 10000017 10000018
10000019 10000020 10000021
10000022 10000023 10000024
10000025 10000026 10000027
10000028 10000029 10000030
10000031 10000032 10000033
idx 3 7 8
idx 2 5 6
idx 1 3 4
*/
Console.ReadKey();
}
}
}
Существует также раунд оптимизаций, которые вы могли бы ввести для нестандартной итерации кусков, которая здесь отсутствует.
Каким образом вы должны выбрать? Это полностью зависит от проблемы, которую вы пытаетесь решить. Если вы не заинтересованы в первом недостатке, простой ответ невероятно привлекателен.
Обратите внимание, что как и в большинстве методов, это небезопасно для многопоточности, материал может стать странным, если вы хотите сделать его потокобезопасным необходимо будет исправить EnumeratorWrapper
.
Предположим, что у вас есть
boolean active;
Метод аксессуаров будет
public boolean isActive(){return this.active;}
public void setActive(boolean active){this.active = active;}
См. также
Как установщик, как насчет:
// setter
public void beCurrent(boolean X) {
this.isCurrent = X;
}
или
// setter
public void makeCurrent(boolean X) {
this.isCurrent = X;
}
Я не уверен, что эти обозначения имеют смысл для носителей английского языка.
Чтобы избежать путаницы; Я рекомендую использовать
// Setter method for isCurrent
public void setIsCurrent(boolean isCurrent) {
this.isCurrent = isCurrent;
}
// Getter method for isCurrent
public boolean getIsCurrent() {
return isCurrent;
}
Возможно, пришло время начать пересмотр этого ответа? Лично я проголосовал бы за setActive()
и unsetActive()
(альтернативы могут быть setUnActive()
, notActive()
, disable()
и т. Д. В зависимости от контекста), поскольку «setActive» подразумевает, что вы активируете его в любое время, т. Это своего рода счетчик, интуитивно понятный, чтобы сказать «setActive», но фактически удалить активное состояние.
Другая проблема заключается в том, что вы не можете прослушивать специфическое событие SetActive в режиме CQRS, вам нужно будет прослушать 'setActiveEvent' и определить внутри этого прослушивателя, на самом деле он активен или нет. Или, конечно, определите, какое событие вызывать при вызове setActive()
, но затем идет против принципа «Разделение проблем».
Хорошо читайте об этом в статье FlagArgument Мартина Фаулера: http: / /martinfowler.com/bliki/FlagArgument.html
Однако я исхожу из фона PHP и вижу, что эта тенденция все больше принимается. Не уверен, насколько это связано с развитием Java.
private boolean current;
public void setCurrent(boolean current){
this.current=current;
}
public boolean hasCurrent(){
return this.current;
}
has
используется для BO или такой службы с некоторой обработкой, а для POJO - is
. и, пожалуйста, добавьте описание вашего ответа.
– Al-Mothafar
1 February 2018 в 09:48
http://geosoft.no/development/javastyle.html#Specific
- is следует использовать префикс для булевых переменных и методов. isSet, isVisible, isFinished, isFound, isOpen Это соглашение об именах для булевых методов и переменных, используемых Sun для базовых пакетов Java. Использование префикса решает общую проблему выбора плохих логических имен, таких как статус или флаг. isStatus или isFlag просто не подходит, и программист вынужден выбирать более значимые имена.
Методы сеттера для булевых переменных должны иметь префикс префикса, как в:
void setFound (boolean isFound); Есть несколько альтернатив префиксу, который лучше подходит в некоторых ситуациях. Они имеют, могут и должны иметь префиксы:
boolean hasLicense (); boolean canEvaluate (); boolean shouldAbort = false;
blockquote>
hasData
, что бы выглядел сеттер? Скорее всего, setData(bool hasData)
выглядит ужасно неправильным для меня ...
– Franz B.
25 January 2016 в 10:34
has
, can
, should
не являются частью спецификации. Ссылка Спецификация JavaBeans 1.01 , раздел 8.3.
– VCD
1 August 2016 в 07:16
Для поля с именем isCurrent
правильное присвоение геттера / сеттера - setCurrent()
/ isCurrent()
(по крайней мере, это то, что думает Eclipse), что очень сбивает с толку и может быть прослежено до основной проблемы:
В первую очередь ваше поле не должно называться isCurrent
. Является ли глаголом, а глаголы не соответствуют представлению состояния объекта. Вместо этого используйте прилагательное, и ваши имена получателя / сеттера будут иметь смысл:
private boolean current;
public boolean isCurrent(){
return current;
}
public void setCurrent(final boolean current){
this.current = current;
}
Setter: public void setCurrent(boolean val)
Getter: public boolean getCurrent()
Для booleans вы также можете использовать
public boolean isCurrent()
hasCustomName
. Теперь, что я должен назвать для методов getter и setter ? Хорошо лиsetHasCustomName[setter]
иhasCustomName[getter]
? – Hadi 29 July 2018 в 08:50