MATSIM
AttributesImpl.java
Go to the documentation of this file.
1 
2 /* *********************************************************************** *
3  * project: org.matsim.*
4  * Attributes.java
5  * *
6  * *********************************************************************** *
7  * *
8  * copyright : (C) 2019 by the members listed in the COPYING, *
9  * LICENSE and WARRANTY file. *
10  * email : info at matsim dot org *
11  * *
12  * *********************************************************************** *
13  * *
14  * This program is free software; you can redistribute it and/or modify *
15  * it under the terms of the GNU General Public License as published by *
16  * the Free Software Foundation; either version 2 of the License, or *
17  * (at your option) any later version. *
18  * See also COPYING, LICENSE and WARRANTY file *
19  * *
20  * *********************************************************************** */
21 
22  package org.matsim.utils.objectattributes.attributable;
23 
24 import java.util.*;
25 
31 public final class AttributesImpl implements Attributes {
32  // there are potentially lots of instance of this class, containing typically few mappings each.
33  // to minimize memory footprint, values are stored in arrays, kept as short as possible.
34  // This makes insertion costly, but query can be kept efficient even when the number of mappings
35  // increases, using binary search. This should be fine, as the typical usage is to set once and
36  // access often. Replacing a value is also efficient.
37  //
38  // In addition, as lots of classes implement Attributable, there might be a large number of empty attributes,
39  // which would result in unnecessary memory overhead if each attribute would use new instances of empty arrays
40  // (Which are essentially immutable objects), hence the two "empty" constants (idea from Marcel Rieser, see MATSIM-811)
41  private static final String[] EMPTY_KEYS = new String[0];
42  private static final Object[] EMPTY_VALUES = new Object[0];
43 
44  private String[] keys = EMPTY_KEYS;
45  private Object[] values = EMPTY_VALUES;
46 
47  @Override
48  public String toString() {
49  StringBuilder stb = new StringBuilder() ;
50  for ( int i=0; i < keys.length; i++ ) {
51  String subkey = keys[ i ];
52  stb.append("{ key=").append(subkey);
53  stb.append("; object=").append( values[ i ].toString());
54  stb.append( " }" );
55  }
56  return stb.toString() ;
57  }
58 
59  public Object putAttribute( final String attribute, final Object value) {
60  final int insertion = Arrays.binarySearch( keys , attribute );
61 
62  if ( insertion >= 0 ) {
63  final Object prev = values[ insertion ];
64  values[ insertion ] = value;
65  return prev;
66  }
67 
68  final int newIndex = -insertion - 1;
69 
70  keys = Arrays.copyOf( keys , keys.length + 1 );
71  values = Arrays.copyOf( values , values.length + 1 );
72 
73  for ( int i=keys.length - 2; i >= newIndex; i-- ) {
74  keys[ i + 1 ] = keys[ i ];
75  values[ i + 1 ] = values[ i ];
76  }
77 
78  keys[newIndex] = attribute;
79  values[newIndex] = value;
80 
81  return null;
82  }
83 
84  public Object getAttribute( final String attribute) {
85  final int insertion = Arrays.binarySearch( keys , attribute );
86 
87  if ( insertion < 0 ) return null;
88 
89  return values[ insertion ];
90  }
91 
92  public Object removeAttribute( final String attribute ) {
93  final int insertion = Arrays.binarySearch( keys , attribute );
94 
95  if ( insertion < 0 ) return null;
96 
97  final Object prev = values[ insertion ];
98 
99  for ( int i=insertion; i < keys.length - 1; i++ ) {
100  keys[ i ] = keys[ i + 1 ];
101  values[ i ] = values[ i + 1 ];
102  }
103 
104  keys = Arrays.copyOf( keys , keys.length - 1 );
105  values = Arrays.copyOf( values , values.length - 1 );
106 
107  return prev;
108  }
109 
110  public void clear() {
111  keys = EMPTY_KEYS;
112  values = EMPTY_VALUES;
113  }
114 
123  public Map<String, Object> getAsMap() {
124  return new AbstractMap<String, Object>() {
125  @Override
126  public Set<Entry<String, Object>> entrySet() {
127  return new AbstractSet<Entry<String, Object>>() {
128  @Override
129  public Iterator<Entry<String, Object>> iterator() {
130  return new EntryIterator();
131  }
132 
133  @Override
134  public int size() {
135  return keys.length;
136  }
137  };
138  }
139  };
140  }
141 
142  public int size() {
143  return keys.length;
144  }
145 
146  public boolean isEmpty() {
147  return size() == 0;
148  }
149 
150  private class EntryIterator implements Iterator<Map.Entry<String, Object>> {
151  private int index = 0;
152 
153  @Override
154  public boolean hasNext() {
155  return index < keys.length;
156  }
157 
158  @Override
159  public Map.Entry<String, Object> next() {
160  if (index >= keys.length) {
161  throw new NoSuchElementException();
162  }
163  Map.Entry<String, Object> entry = new AbstractMap.SimpleEntry<>(keys[index], values[index]) ;
164  index++;
165  return entry;
166  }
167  }
168 }
Object putAttribute(final String attribute, final Object value)
Map.Entry< String, Object > next()
boolean hasNext()
int index