1919
2020public final class Attributes implements Iterable <AttributeNode > {
2121
22+ private AttributeNode [] smallAttributes ;
23+ private int size ;
2224 private Map <String , AttributeNode > attributes ;
2325
2426 public Attributes () {
@@ -29,7 +31,17 @@ public Attributes(Map<String, AttributeNode> attributes) {
2931 }
3032
3133 public boolean containsKey (String key ) {
32- return attributes != null && attributes .containsKey (key );
34+ if (attributes != null ) {
35+ return attributes .containsKey (key );
36+ }
37+ if (smallAttributes != null ) {
38+ for (int i = 0 ; i < size ; i ++) {
39+ if (smallAttributes [i ].name .equals (key )) {
40+ return true ;
41+ }
42+ }
43+ }
44+ return false ;
3345 }
3446
3547 @ Override
@@ -39,67 +51,172 @@ public boolean equals(Object obj) {
3951 }
4052
4153 if (obj instanceof Attributes a ) {
42- return Objects .equals (attributes , a .attributes );
54+ if (attributes != null || a .attributes != null ) {
55+ return Objects .equals (asMap (), a .asMap ());
56+ }
57+ if (size != a .size ) {
58+ return false ;
59+ }
60+ for (int i = 0 ; i < size ; i ++) {
61+ if (!smallAttributes [i ].equals (a .smallAttributes [i ])) {
62+ return false ;
63+ }
64+ }
65+ return true ;
4366 }
4467 return false ;
4568 }
4669
70+ private Map <String , AttributeNode > asMap () {
71+ if (attributes != null ) {
72+ return attributes ;
73+ }
74+ if (size == 0 ) {
75+ return Collections .emptyMap ();
76+ }
77+ Map <String , AttributeNode > m = new LinkedHashMap <>();
78+ for (int i = 0 ; i < size ; i ++) {
79+ m .put (smallAttributes [i ].name , smallAttributes [i ]);
80+ }
81+ return m ;
82+ }
83+
4784 @ Override
4885 public int hashCode () {
49- return Objects .hashCode (attributes );
86+ if (attributes != null ) {
87+ return Objects .hashCode (attributes );
88+ }
89+ int hash = 7 ;
90+ for (int i = 0 ; i < size ; i ++) {
91+ hash = 31 * hash + smallAttributes [i ].hashCode ();
92+ }
93+ return hash ;
5094 }
5195
5296 public Attributes copy () {
5397 Attributes a = new Attributes ();
5498 if (attributes != null ) {
99+ a .attributes = new LinkedHashMap <>();
55100 for (var v : attributes .values ()) {
56101 a .put (new AttributeNode (v ));
57102 }
58- return a ;
103+ } else if (smallAttributes != null ) {
104+ a .smallAttributes = new AttributeNode [smallAttributes .length ];
105+ a .size = size ;
106+ for (int i = 0 ; i < size ; i ++) {
107+ a .smallAttributes [i ] = new AttributeNode (smallAttributes [i ]);
108+ }
59109 }
60110 return a ;
61111 }
62112
63113 public AttributeNode get (String key ) {
64- return attributes == null ? null : attributes .get (key );
114+ if (attributes != null ) {
115+ return attributes .get (key );
116+ }
117+ if (smallAttributes != null ) {
118+ for (int i = 0 ; i < size ; i ++) {
119+ if (smallAttributes [i ].name .equals (key )) {
120+ return smallAttributes [i ];
121+ }
122+ }
123+ }
124+ return null ;
65125 }
66126
67127 Set <String > keySet () {
68- return attributes == null ? Set .of () : attributes .keySet ();
69- }
70-
71- private void ensureMap () {
72- if (attributes == null ) {
73- attributes = new LinkedHashMap <>();
128+ if (attributes != null ) {
129+ return attributes .keySet ();
130+ }
131+ if (size == 0 ) {
132+ return Collections .emptySet ();
74133 }
134+ Set <String > s = new LinkedHashSet <>();
135+ for (int i = 0 ; i < size ; i ++) {
136+ s .add (smallAttributes [i ].name );
137+ }
138+ return s ;
75139 }
76140
77141 public void put (AttributeNode attribute ) {
78- ensureMap ();
79- attributes .put (attribute .getName (), attribute );
142+ if (attributes != null ) {
143+ attributes .put (attribute .getName (), attribute );
144+ return ;
145+ }
146+
147+ if (smallAttributes == null ) {
148+ smallAttributes = new AttributeNode [2 ];
149+ }
150+
151+ for (int i = 0 ; i < size ; i ++) {
152+ if (smallAttributes [i ].name .equals (attribute .name )) {
153+ smallAttributes [i ] = attribute ;
154+ return ;
155+ }
156+ }
157+
158+ if (size < 8 ) {
159+ if (size == smallAttributes .length ) {
160+ smallAttributes = Arrays .copyOf (smallAttributes , smallAttributes .length * 2 );
161+ }
162+ smallAttributes [size ++] = attribute ;
163+ } else {
164+ attributes = new LinkedHashMap <>();
165+ for (int i = 0 ; i < size ; i ++) {
166+ attributes .put (smallAttributes [i ].name , smallAttributes [i ]);
167+ }
168+ attributes .put (attribute .name , attribute );
169+ smallAttributes = null ;
170+ }
80171 }
81172
82173 public void put (String key , String value ) {
83- ensureMap ();
84- attributes .put (key , new AttributeNode (key , value ));
174+ put (new AttributeNode (key , value ));
85175 }
86176
87177 public void remove (String key ) {
88178 if (attributes != null ) {
89179 attributes .remove (key );
180+ } else if (smallAttributes != null ) {
181+ for (int i = 0 ; i < size ; i ++) {
182+ if (smallAttributes [i ].name .equals (key )) {
183+ System .arraycopy (smallAttributes , i + 1 , smallAttributes , i , size - i - 1 );
184+ smallAttributes [--size ] = null ;
185+ return ;
186+ }
187+ }
90188 }
91189 }
92190
93191 public boolean isEmpty () {
94- return attributes == null || attributes .isEmpty ();
192+ return ( attributes == null || attributes .isEmpty ()) && size == 0 ;
95193 }
96194
97195 @ Override
98196 public Iterator <AttributeNode > iterator () {
99- return attributes == null ? Collections .emptyIterator () : attributes .values ().iterator ();
197+ if (attributes != null ) {
198+ return attributes .values ().iterator ();
199+ }
200+ if (size == 0 ) {
201+ return Collections .emptyIterator ();
202+ }
203+ return new Iterator <>() {
204+ int i = 0 ;
205+
206+ @ Override
207+ public boolean hasNext () {
208+ return i < size ;
209+ }
210+
211+ @ Override
212+ public AttributeNode next () {
213+ return smallAttributes [i ++];
214+ }
215+ };
100216 }
101217
102218 public String getNamedItem (String name ) {
103- return attributes != null && attributes .containsKey (name ) ? attributes .get (name ).getValue () : null ;
219+ AttributeNode a = get (name );
220+ return a != null ? a .getValue () : null ;
104221 }
105222}
0 commit comments