File Coverage

blib/lib/Data/ArrayList.pm
Criterion Covered Total %
statement 8 10 80.0
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 12 14 85.7


line stmt bran cond sub pod time code
1 2     2   2827 use strict;
  2         5  
  2         102  
2 2     2   14 use warnings;
  2         4  
  2         141  
3             package Data::ArrayList;
4             BEGIN {
5 2     2   38 $Data::ArrayList::VERSION = '0.01';
6             }
7             # ABSTRACT: java.util.ArrayList for perl
8              
9 2     2   11339 use Moose;
  0            
  0            
10             use Data::Clone ();
11              
12             use Data::ArrayList::ListIterator;
13              
14              
15             has '_data' => (
16             is => 'ro',
17             isa => 'ArrayRef[Any]',
18             traits => [qw( Array )],
19             default => sub { [] },
20             handles => {
21             '_addAt' => 'splice',
22             '_set' => 'set',
23             '_get' => 'get',
24             '_clear' => 'clear',
25             '_delete' => 'delete',
26             },
27             );
28              
29             has '_size' => (
30             is => 'rw',
31             isa => 'Int',
32             traits => [qw( Counter )],
33             default => 0,
34             handles => {
35             '_inc_size' => 'inc',
36             '_dec_size' => 'dec',
37             '_reset_size' => 'reset',
38             },
39             );
40              
41             has '_mod_count' => (
42             is => 'rw',
43             isa => 'Int',
44             traits => [qw( Counter )],
45             default => 0,
46             handles => {
47             '_inc_modcount' => 'inc',
48             },
49             );
50              
51             around BUILDARGS => sub {
52             my $next = shift;
53             my $class = shift;
54              
55             return $class->$next({ _initialCapacity => @_ || 10 });
56             };
57              
58             sub BUILD {
59             my ( $self, $params ) = @_;
60              
61             # ensureCapacity only on parent objects
62             if ( ref $self eq __PACKAGE__ ) {
63             $self->ensureCapacity( $params->{_initialCapacity} );
64             };
65              
66             return $self;
67             };
68              
69              
70             sub add {
71             my $self = shift;
72              
73             die "IllegalArgument"
74             unless scalar @_;
75              
76             $self->_addAt( $self->_size, 0, @_ );
77              
78             $self->_inc_size( scalar @_ );
79             $self->_inc_modcount;
80              
81             return 1;
82             }
83              
84              
85             sub addAt {
86             my $self = shift;
87             my $index = shift;
88              
89             die "IndexOutOfBounds"
90             unless $self->_checkIndexForAdd( $index );
91              
92             die "IllegalArgument"
93             unless scalar @_;
94              
95             $self->_addAt( $index, 0, @_ );
96              
97             $self->_inc_size( scalar @_ );
98             $self->_inc_modcount;
99              
100             return 1;
101             }
102              
103              
104             sub get {
105             my $self = shift;
106             my $index = shift;
107              
108             die "IndexOutOfBounds"
109             unless $self->_checkIndex( $index );
110              
111             return $self->_get($index);
112             }
113              
114              
115             sub addAll {
116             shift->add( @_ );
117             }
118              
119              
120             sub addAllAt {
121             shift->addAt( @_ );
122             }
123              
124              
125             sub clear {
126             my $self = shift;
127              
128             $self->_clear;
129             $self->_reset_size;
130             $self->_inc_modcount;
131             }
132              
133              
134              
135             sub isEmpty {
136             return $_[0]->size == 0;
137             }
138              
139              
140             sub indexOf {
141             my $self = shift;
142             my $comparator = shift;
143              
144             for my $i ( 0 .. ($self->size - 1) ) {
145             local *_ = \( $self->get($i) );
146             return $i if $comparator->();
147             }
148             return -1;
149             }
150              
151              
152             sub lastIndexOf {
153             my $self = shift;
154             my $comparator = shift;
155              
156             for my $i ( reverse 0 .. ($self->size - 1) ) {
157             local *_ = \( $self->get($i) );
158             return $i if $comparator->();
159             }
160             return -1;
161             }
162              
163              
164             sub contains {
165             shift->indexOf(@_) >= 0;
166             }
167              
168              
169             sub size {
170             return $_[0]->_size;
171             }
172              
173              
174             sub clone {
175             my $self = shift;
176              
177             return bless { %$self }, ref $self;
178             }
179              
180              
181             sub toArray {
182             return @{ Data::Clone::clone($_[0]->_data) };
183             }
184              
185              
186             sub set {
187             my $self = shift;
188             my $index = shift;
189              
190             die "IllegalArgument"
191             unless scalar @_;
192              
193             my $value = shift;
194              
195             die "IndexOutOfBounds"
196             unless $self->_checkIndex( $index );
197              
198             my $old = $self->get($index);
199             $self->_set( $index, $value );
200              
201             return $old;
202             }
203              
204              
205             sub ensureCapacity {
206             my $self = shift;
207             my $capacity = shift;
208              
209             die "IllegalArgument"
210             unless $capacity;
211              
212             my $size = $self->size;
213             if ( $capacity > $size ) {
214             my $d = $self->_data;
215             $d->[$capacity] = undef;
216             delete $d->[$capacity];
217             return 1;
218             };
219             }
220              
221              
222             sub remove {
223             my $self = shift;
224              
225             die "IllegalArgument"
226             unless scalar @_;
227             my $index = shift;
228              
229             die "IndexOutOfBounds"
230             unless $self->_checkIndex( $index );
231              
232             my $old = $self->get($index);
233             $self->_delete( $index );
234              
235             $self->_dec_size;
236             $self->_inc_modcount;
237              
238             return $old;
239             }
240              
241             sub _removeRange {
242             my $self = shift;
243             my $rangeFrom = shift;
244             my $rangeTo = shift;
245              
246              
247             $self->_addAt( $rangeFrom, $rangeTo - $rangeFrom );
248              
249             $self->_dec_size( $rangeTo - $rangeFrom );
250             $self->_inc_modcount;
251              
252             return 1;
253             }
254              
255              
256             sub listIterator {
257             my $self = shift;
258              
259             my $initialPosition = shift || 0;
260              
261             die "IndexOutOfBounds"
262             unless $self->_checkIndex( $initialPosition );
263              
264             my $iter = Data::ArrayList::ListIterator->new(
265             _mod_count => $self->_mod_count,
266             _parent => $self,
267             _cursor => $initialPosition,
268             );
269              
270             return $iter;
271             }
272              
273              
274             sub subList {
275             my $SELF = shift;
276             my $rangeFrom = shift;
277             my $rangeTo = shift;
278             my $offset = shift || 0;
279              
280             die "IllegalArgument"
281             unless defined $rangeFrom
282             && $rangeTo;
283              
284             die "IndexOutOfBounds" unless
285             $SELF->_checkSubListRange( $rangeFrom, $rangeTo );
286              
287             my $sl_meta = Moose::Meta::Class->create_anon_class(
288             superclasses => [ $SELF->meta->name ],
289             );
290              
291             $sl_meta->add_attribute(
292             _parentOffset => (
293             isa => 'Int',
294             default => $rangeFrom,
295             is => 'ro',
296             )
297             );
298             $sl_meta->add_attribute(
299             _offset => (
300             isa => 'Int',
301             default => $offset + $rangeFrom,
302             is => 'ro',
303             )
304             );
305             $sl_meta->add_attribute(
306             _parent => (
307             isa => 'Data::ArrayList',
308             default => sub { $SELF },
309             is => 'rw',
310             )
311             );
312             $sl_meta->add_method('_checkCoMod', Class::MOP::Method->wrap(
313             name => '_checkCoMod',
314             package_name => $sl_meta->name,
315             body => sub {
316             return $_[0]->_mod_count == $SELF->_mod_count;
317             },
318             )
319             );
320              
321             $sl_meta->add_around_method_modifier('set',
322             sub {
323             shift; # next
324             my $self = shift;
325              
326             die "IllegalArgument"
327             unless scalar @_;
328              
329             my $index = shift;
330              
331             die "ConcurrentModification" unless
332             $self->_checkCoMod;
333              
334             die "IndexOutOfBounds"
335             unless $self->_checkIndex( $index );
336              
337             return $SELF->set( $self->_offset + $index, @_ );
338             }
339             );
340             $sl_meta->add_around_method_modifier('get',
341             sub {
342             shift; # next
343             my $self = shift;
344             my $index = shift;
345              
346             die "ConcurrentModification" unless
347             $self->_checkCoMod;
348              
349             die "IndexOutOfBounds"
350             unless $self->_checkIndex( $index );
351              
352             return $SELF->get( $self->_offset + $index );
353             }
354             );
355              
356             $sl_meta->add_around_method_modifier('size',
357             sub {
358             my $next = shift;
359             my $self = shift;
360              
361             die "ConcurrentModification" unless
362             $self->_checkCoMod;
363              
364             return $self->$next();
365             }
366             );
367             $sl_meta->add_around_method_modifier('add',
368             sub {
369             shift; # next
370             my $self = shift;
371              
372             $self->addAt( $self->size, @_ );
373              
374             return 1;
375             }
376             );
377              
378             $sl_meta->add_around_method_modifier('addAt',
379             sub {
380             shift; # next
381             my $self = shift;
382             my $index = shift;
383              
384             die "ConcurrentModification" unless
385             $self->_checkCoMod;
386              
387             die "IndexOutOfBounds"
388             unless $self->_checkIndexForAdd( $index );
389              
390             $self->_parent->addAt( $self->_parentOffset + $index, @_ );
391              
392             $self->_inc_size( scalar @_ );
393              
394             $self->_mod_count( $self->_parent->_mod_count );
395             }
396             );
397             $sl_meta->add_around_method_modifier('remove',
398             sub {
399             shift; # next
400             my $self = shift;
401              
402             die "IllegalArgument"
403             unless scalar @_;
404              
405             my $index = shift;
406              
407             die "ConcurrentModification" unless
408             $self->_checkCoMod;
409              
410             die "IndexOutOfBounds"
411             unless $self->_checkIndex( $index );
412              
413             my $old = $self->_parent->remove( $self->_parentOffset + $index );
414              
415             $self->_dec_size();
416              
417             $self->_mod_count( $self->_parent->_mod_count );
418              
419             return $old;
420             }
421             );
422              
423              
424             $sl_meta->add_around_method_modifier('clear',
425             sub {
426             shift; # next
427             my $self = shift;
428              
429             return $self->_removeRange( 0, $self->size );
430             }
431             );
432              
433             $sl_meta->add_around_method_modifier('_removeRange',
434             sub {
435             shift; # next
436             my $self = shift;
437             my $rangeFrom = shift;
438             my $rangeTo = shift;
439              
440             $self->_parent->_removeRange(
441             $self->_parentOffset + $rangeFrom,
442             $self->_parentOffset + $rangeTo,
443             );
444              
445             $self->_mod_count( $self->_parent->_mod_count );
446             $self->_dec_size( $rangeTo - $rangeFrom );
447              
448             return 1;
449             }
450             );
451             $sl_meta->add_around_method_modifier('toArray',
452             sub {
453             shift; # next
454             my $self = shift;
455              
456             die "ConcurrentModification" unless
457             $self->_checkCoMod;
458              
459             my $d = $SELF->_data;
460             return @{ Data::Clone::clone([ @$d[ $self->_offset .. $self->size ]]) };
461             }
462             );
463             $sl_meta->add_around_method_modifier('ensureCapacity',
464             sub {
465             die "UnsupportedOperationException";
466             }
467             );
468             $sl_meta->add_around_method_modifier('subList',
469             sub {
470             shift; # next
471             my $self = shift;
472             my $rangeFrom = shift;
473             my $rangeTo = shift;
474              
475             die "ConcurrentModification" unless
476             $self->_checkCoMod;
477              
478             die "IndexOutOfBounds" unless
479             $self->_checkSubListRange( $rangeFrom, $rangeTo );
480              
481             my $sublist = $SELF->subList($rangeFrom, $rangeTo, $self->_offset);
482             $sublist->_parent( $self );
483              
484             return $sublist;
485             }
486             );
487              
488             $sl_meta->make_immutable;
489              
490             my $sublist = $sl_meta->new_object();
491              
492             $sublist->_size($rangeTo - $rangeFrom);
493             $sublist->_mod_count($SELF->_mod_count);
494              
495             return $sublist;
496             }
497              
498             sub _checkIndex {
499             my $self = shift;
500             my $index = shift;
501              
502             return $index < 0 || $index >= $self->size ? 0 : 1;
503             }
504              
505             sub _checkIndexForAdd {
506             my $self = shift;
507             my $index = shift;
508              
509             return $index < 0 || $index > $self->size ? 0 : 1;
510             }
511              
512             sub _checkSubListRange {
513             my $self = shift;
514             my $rangeFrom = shift;
515             my $rangeTo = shift;
516              
517             return $rangeFrom < 0
518             || $rangeTo > $self->size
519             || $rangeFrom > $rangeTo ? 0 : 1;
520             }
521              
522              
523             __PACKAGE__->meta->make_immutable;
524              
525             1;
526              
527             __END__
528             =pod
529              
530             =encoding utf-8
531              
532             =head1 NAME
533              
534             Data::ArrayList - java.util.ArrayList for perl
535              
536             =head1 VERSION
537              
538             version 0.01
539              
540             =head1 SYNOPSIS
541              
542             use Data::ArrayList;
543              
544             my $dal = Data::ArrayList->new( my $initialCapacity = 20 );
545              
546             say "is empty" if $dal->isEmpty;
547              
548             $dal->add("at the end");
549              
550             $dal->addAll( 1 .. 100 );
551              
552             $dal->add("at the end");
553              
554             say $dal->get( 12 );
555             # prints 12
556              
557             $dal->set(12, "I was 12 before");
558              
559             say $dal->indexOf(sub { /^at the end$/ });
560             # prints 0
561              
562             say $dal->lastIndexOf(sub { /^at the end$/ });
563             # prints 101
564              
565             my $shallowcopy = $dal->clone;
566              
567             my @deepcopyofelements = $dal->toArray();
568              
569             $dal->ensureCapacity( 1_999_999 );
570             $dal->addAll( 1 .. 1_000_000 );
571              
572             say $dal->size;
573             # prints 1000102
574              
575             $dal->remove( 12 );
576              
577             say $dal->get( 12 );
578             # prints 13
579              
580             my $sublist = $dal->subList( 101, 1_000_101 );
581             $sublist->clear;
582              
583             say $dal->size;
584             # prints 101
585              
586             my $iter = $dal->listIterator();
587              
588             while ( $iter->hasNext ) {
589             my $idx = $iter->nextIndex;
590             my $elem = $iter->next;
591              
592             $iter->add( "$elem from $idx again" );
593             }
594              
595             while ( $iter->hasPrevious ) {
596             my $idx = $iter->previousIndex;
597              
598             my $elem = $iter->previous;
599              
600             $iter->remove if $elem =~ / again$/;
601             }
602              
603             $dal->clear;
604              
605             say $dal->size;
606             # prints 0
607              
608             =head1 DESCRIPTION
609              
610             Data::ArrayList is a perl port of I<java.util.ArrayList> with some of the methods
611             inherited from I<java.util.AbstractList>.
612              
613             Please note that the author strongly encourages users of this module to read
614             L<perlfunc/"Perl Functions by Category "> I<Functions for real @ARRAYs>,
615             as use of this module introduces significant performance penalties (non-OO
616             with native functions is at least twice as fast).
617              
618             However he believes that chance of converting Java developers to perl is worth
619             existence of this module.
620              
621             Besides it was also fun to write ;-)
622              
623             =head1 METHODS
624              
625             =head2 add
626              
627             $dal->add( $element );
628              
629             Appends the specified element to the end of this list.
630              
631             =head2 addAt
632              
633             $dal->addAt( $index, $element );
634              
635             Inserts the specified element at the specified position in this list. Shifts
636             the element currently at that position (if any) and any subsequent elements
637             to the right (adds one to their indices).
638              
639             =head2 get
640              
641             my $element = $dal->get( $index );
642              
643             Returns the element at the specified position in this list.
644              
645             =head2 addAll
646              
647             $dal->addAll( @elements );
648              
649             Appends all of the specified elements to the end of this list, in their current
650             order.
651              
652             =head2 addAllAt
653              
654             $dal->addAllAt( $index, @elements );
655              
656             Inserts all of the specified elements into this list, starting at the specified
657             position. Shifts the element currently at that position (if any) and any
658             subsequent elements to the right (increases their indices). The new elements
659             will appear in the list in their current order.
660              
661             =head2 clear
662              
663             $dal->clear;
664              
665             Removes all of the elements from this list. The list will be empty after this
666             call returns.
667              
668             =head2 isEmpty
669              
670             $dal->isEmpty;
671              
672             Returns I<true> if this list contains no elements.
673              
674             =head2 indexOf
675              
676             my $index = $dal->indexOf( sub { $_ =~ /^value$/ } );
677              
678             Returns the index of the first occurrence in this list of the element for
679             which the specified anonymous sub returns true, or -1 if this list does not
680             contain the element.
681              
682             =head2 lastIndexOf
683              
684             my $index = $dal->lastIndexOf( sub { $_ =~ /^value$/ } );
685              
686             Returns the index of the last occurrence in this list of the element for
687             which the specified anonymous sub returns true, or -1 if this list does not
688             contain the element.
689              
690             =head2 contains
691              
692             $dal->contains( sub { $_ =~ /^value$/ } );
693              
694             Returns I<true> if the list contains an element for which the specified
695             anonymous sub returns true.
696              
697             =head2 size
698              
699             my $size_of_list = $dal->size;
700              
701             Returns the number of elements in this list.
702              
703             =head2 clone
704              
705             my $copy = $dal->clone;
706              
707             Returns a shallow copy of this instance.
708             The elements themselves are not copied.
709              
710             =head2 toArray
711              
712             my @elements = $dal->toArray;
713              
714             Returns an array containing all of the elements in this list in proper
715             sequence (from first to last element).
716              
717             The returned array will be "safe" in that no references to it are maintained
718             by this list. (In other words, this method must allocate a new array). The
719             caller is thus free to modify the returned array.
720              
721             B<Note:> The I<safeness> of the copy is provided by L<Data::Clone>. Please make
722             sure that all blessed objects implement C<clone> to support deep cloning.
723              
724             =head2 set
725              
726             $dal->set( $index, $value );
727              
728             Replaces the element at the specified position in this list with the specified
729             element.
730              
731             Returns the element previously at the specified position.
732              
733             =head2 ensureCapacity
734              
735             $dal->ensureCapacity( $minCapacity );
736              
737             Increases the capacity of this instance, if necessary, to ensure that it can
738             hold at least the number of elements specified by the minimum capacity
739             argument.
740              
741             B<Note:> This method is not supported by objects returned by L<"subList">.
742              
743             =head2 remove
744              
745             $dal->remove( $index );
746              
747             Removes the element at the specified position in this list. Shifts any
748             subsequent elements to the left (subtracts one from their indices).
749              
750             Returns the element that was removed from the list.
751              
752             =head2 listIterator
753              
754             my $li = $dal->listIterator( $initialPosition );
755              
756             Returns a list iterator (L<Data::ArrayList::ListIterator>) of the elements in
757             this list (in proper sequence), starting at the specified position
758             (I<default is 0>) in this list. The specified index indicates the first element
759             that would be returned by an initial call to next. An initial call to previous
760             would return the element with the specified index minus one.
761              
762             Iterator will die with C<ConcurrentModification> if the parent list has been
763             I<structurally modified>. Structural modifications are those that change the
764             size of the list, or otherwise perturb it in such a fashion that iterations in
765             progress may yield incorrect results.
766              
767             =head2 subList
768              
769             my $sl = $dal->subList( $rangeFrom, $rangeTo );
770              
771             Returns a view of the portion of this list between the specified C<rangeFrom>,
772             inclusive, and C<rangeTo>, exclusive. (If C<rangeFrom> and C<rangeTo> are
773             equal, the returned list is empty.) The returned list is backed by this list,
774             so non-structural changes in the returned list are reflected in this list, and
775             vice-versa. The returned list supports all of the optional list operations
776             supported by this list.
777              
778             This method eliminates the need for explicit range operations (of the sort that
779             commonly exist for arrays). Any operation that expects a list can be used as a
780             range operation by passing a subList view instead of a whole list. For example,
781             the following idiom removes a range of elements from a list:
782              
783             $dal->subList($from, $to)->clear();
784              
785             Returned sublist is a subclass of L<Data::ArrayList> and supports all of its
786             methods (except the L<"ensureCapacity">).
787              
788             Sublists could be nested, as in:
789              
790             $dal->subList( 1, 100 )->subList( 20, 20 );
791              
792             =for Pod::Coverage BUILD
793              
794             =head1 SEE ALSO
795              
796             =over 4
797              
798             =item *
799              
800             L<perlfunc>
801              
802             =item *
803              
804             L<List::MoreUtils>
805              
806             =item *
807              
808             L<Data::Clone>
809              
810             =item *
811              
812             L<http://download.oracle.com/javase/6/docs/api/java/util/ArrayList.html>
813              
814             =back
815              
816             =head1 AUTHOR
817              
818             Alex J. G. BurzyÅ„ski <ajgb@cpan.org>
819              
820             =head1 COPYRIGHT AND LICENSE
821              
822             This software is copyright (c) 2010 by Alex J. G. BurzyÅ„ski <ajgb@cpan.org>.
823              
824             This is free software; you can redistribute it and/or modify it under
825             the same terms as the Perl 5 programming language system itself.
826              
827             =cut
828