Revert "Make LinkedHashMap also have a factory constructor and be customizable"

Dartium expectations expects "LinkedHashMap" and gets "_LinkedHashMap".

Review URL: https://codereview.chromium.org//23890008

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@27250 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
lrn@google.com 2013-09-06 14:29:04 +00:00
parent dff262d761
commit 761f55b1b9
13 changed files with 374 additions and 376 deletions

View file

@ -31,10 +31,13 @@ bool isInstanceOf(o, Type t) {
if (oTypeName == tTypeName) {
return true;
}
if (oTypeName.startsWith("List") && tTypeName == "List") {
if (oTypeName.startsWith("HashMap") && tTypeName == "Map") {
return true;
}
if (tTypeName == "Map" && o is Map) {
if (oTypeName.startsWith("LinkedHashMap") && tTypeName == "Map") {
return true;
}
if (oTypeName.startsWith("List") && tTypeName == "List") {
return true;
}
// Dart Analysis Engine specific

View file

@ -344,7 +344,9 @@ class _TemplateIterator {
return;
}
var instanceCache = new HashMap(equals: identical);
// TODO(jmesserly): IdentityMap matches JS semantics, but it's O(N) right
// now. See http://dartbug.com/4161.
var instanceCache = new IdentityMap();
var removeDelta = 0;
for (var splice in splices) {
for (int i = 0; i < splice.removedCount; i++) {

View file

@ -40,7 +40,7 @@ class Writer implements ReaderOrWriter {
* but also serves to record which objects we have already seen.
*/
final Map<dynamic, Reference> references =
new HashMap<Object, Reference>(equals: identical);
new IdentityMap<Object, Reference>();
/**
* The state of objects that need to be serialized is stored here.

View file

@ -180,3 +180,64 @@ class _Sentinel {
final _wrappedObject;
const _Sentinel(this._wrappedObject);
}
/**
* This is used in the implementation of [IdentityMap]. We wrap all the keys
* in an [_IdentityMapKey] that compares using the identity of the wrapped
* objects. It also treats equal primitive values as identical
* to conserve space.
*/
class _IdentityMapKey {
_IdentityMapKey(this._value);
var _value;
/**
* Check if an object is primitive to know if we should compare it using
* equality or identity. We don't test null/true/false where it's the same.
*/
_isPrimitive(x) => x is String || x is num;
operator ==(_IdentityMapKey w) =>
_isPrimitive(_value) ? _value == w._value : identical(_value, w._value);
get hashCode => _value.hashCode;
get object => _value;
}
/**
* This provides an identity map. We wrap all the objects in
* an [_IdentityMapKey] that compares using the identity of the
* wrapped objects. It also treats equal primitive values as identical
* to conserve space.
*/
class IdentityMap<K, V> extends LinkedHashMap<K, V> {
// TODO(alanknight): Replace with a system identity-based map once
// one is available. Issue 4161.
// TODO(lrn): Replace with identity map when custom hash maps are introduced
// (which is soon).
// Check before wrapping because some methods may call others, e.g. on
// dart2js putIfAbsent calls containsKey, so without this we wrap forever.
_wrap(Object key) =>
(key is _IdentityMapKey) ? key : new _IdentityMapKey(key);
_unwrap(_IdentityMapKey wrapper) => wrapper.object;
Iterable<K> get keys => super.keys.map((x) => _unwrap(x));
Iterable<V> get values => super.values;
void forEach(void f(K key, V value)) {
super.forEach((k, v) => f(_unwrap(k), v));
}
V operator [](K key) => super[_wrap(key)];
void operator []=(K key, V value) {
super[_wrap(key)] = value;
}
V putIfAbsent(K key, Function ifAbsent) =>
super.putIfAbsent(_wrap(key), ifAbsent);
bool containsKey(Object key) => super.containsKey(_wrap(key));
V remove(Object key) => super.remove(_wrap(key));
}

View file

@ -0,0 +1,48 @@
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/**
* Provide a trivial test for identity based hashed collections, which we
* provide here until an implementation is available in the regular libraries.
*/
// TODO(alanknight): Remove once identity-hashed collections are available.
// Issue 4161.
library identity_set_test;
import 'package:unittest/unittest.dart';
import 'package:serialization/src/serialization_helpers.dart';
class Foo {
var x;
Foo(this.x);
int get hashCode => x.hashCode;
bool operator ==(a) => a.x == x;
}
main() {
test('basic', () {
var one = new Foo(3);
var two = new Foo(3);
var map = new Map();
var identityMap = new IdentityMap();
map[one] = one;
map[two] = two;
identityMap[one] = one;
identityMap[two] = two;
expect(map.length, 1);
expect(identityMap.length, 2);
for (var each in identityMap.values) {
expect(each, one);
}
});
test('uniquing primitives', () {
var map = new IdentityMap();
var one = 'one';
var two = new String.fromCharCodes(one.codeUnits);
map[one] = 1;
expect(map[two], 1);
expect(map[one], 1);
});
}

View file

@ -4,35 +4,25 @@
patch class HashMap<K, V> {
/* patch */ factory HashMap({ bool equals(K key1, K key2),
int hashCode(K key),
bool isValidKey(potentialKey) }) {
if (isValidKey == null) {
if (hashCode == null) {
if (equals == null) {
return new _HashMap<K, V>();
}
if (identical(identical, equals)) {
return new _IdentityHashMap<K, V>();
}
hashCode = _defaultHashCode;
} else if (equals == null) {
equals = _defaultEquals;
}
} else {
if (hashCode == null) {
hashCode = _defaultHashCode;
}
int hashCode(K key) }) {
if (hashCode == null) {
if (equals == null) {
equals = _defaultEquals;
return new _HashMapImpl<K, V>();
}
if (identical(identical, equals)) {
return new _IdentityHashMap<K, V>();
}
hashCode = _defaultHashCode;
} else if (equals == null) {
equals = _defaultEquals;
}
return new _CustomHashMap<K, V>(equals, hashCode, isValidKey);
return new _CustomHashMap<K, V>(equals, hashCode);
}
}
const int _MODIFICATION_COUNT_MASK = 0x3fffffff;
class _HashMap<K, V> implements HashMap<K, V> {
class _HashMapImpl<K, V> implements HashMap<K, V> {
static const int _INITIAL_CAPACITY = 8;
int _elementCount = 0;
@ -154,7 +144,11 @@ class _HashMap<K, V> implements HashMap<K, V> {
while (entry != null) {
_HashMapEntry next = entry.next;
if (hashCode == entry.hashCode && entry.key == key) {
_removeEntry(entry, previous, index);
if (previous == null) {
buckets[index] = next;
} else {
previous.next = next;
}
_elementCount--;
_modificationCount =
(_modificationCount + 1) & _MODIFICATION_COUNT_MASK;
@ -172,16 +166,6 @@ class _HashMap<K, V> implements HashMap<K, V> {
_modificationCount = (_modificationCount + 1) & _MODIFICATION_COUNT_MASK;
}
void _removeEntry(_HashMapEntry entry,
_HashMapEntry previousInBucket,
int bucketIndex) {
if (previousInBucket == null) {
_buckets[bucketIndex] = entry.next;
} else {
previousInBucket.next = entry.next;
}
}
void _addEntry(List buckets, int index, int length,
K key, V value, int hashCode) {
_HashMapEntry entry =
@ -217,15 +201,12 @@ class _HashMap<K, V> implements HashMap<K, V> {
String toString() => Maps.mapToString(this);
}
class _CustomHashMap<K, V> extends _HashMap<K, V> {
class _CustomHashMap<K, V> extends _HashMapImpl<K, V> {
final _Equality<K> _equals;
final _Hasher<K> _hashCode;
final _Predicate _validKey;
_CustomHashMap(this._equals, this._hashCode, validKey)
: _validKey = (validKey != null) ? validKey : new _TypeTest<K>().test;
_CustomHashMap(this._equals, this._hashCode);
bool containsKey(Object key) {
if (!_validKey(key)) return false;
int hashCode = _hashCode(key);
List buckets = _buckets;
int index = hashCode & (buckets.length - 1);
@ -238,7 +219,6 @@ class _CustomHashMap<K, V> extends _HashMap<K, V> {
}
V operator[](Object key) {
if (!_validKey(key)) return null;
int hashCode = _hashCode(key);
List buckets = _buckets;
int index = hashCode & (buckets.length - 1);
@ -291,7 +271,6 @@ class _CustomHashMap<K, V> extends _HashMap<K, V> {
}
V remove(Object key) {
if (!_validKey(key)) return null;
int hashCode = _hashCode(key);
List buckets = _buckets;
int index = hashCode & (buckets.length - 1);
@ -300,7 +279,11 @@ class _CustomHashMap<K, V> extends _HashMap<K, V> {
while (entry != null) {
_HashMapEntry next = entry.next;
if (hashCode == entry.hashCode && _equals(entry.key, key)) {
_removeEntry(entry, previous, index);
if (previous == null) {
buckets[index] = next;
} else {
previous.next = next;
}
_elementCount--;
_modificationCount =
(_modificationCount + 1) & _MODIFICATION_COUNT_MASK;
@ -315,7 +298,7 @@ class _CustomHashMap<K, V> extends _HashMap<K, V> {
String toString() => Maps.mapToString(this);
}
class _IdentityHashMap<K, V> extends _HashMap<K, V> {
class _IdentityHashMap<K, V> extends _HashMapImpl<K, V> {
bool containsKey(Object key) {
int hashCode = key.hashCode;
List buckets = _buckets;
@ -389,7 +372,11 @@ class _IdentityHashMap<K, V> extends _HashMap<K, V> {
while (entry != null) {
_HashMapEntry next = entry.next;
if (hashCode == entry.hashCode && identical(entry.key, key)) {
_removeEntry(entry, previous, index);
if (previous == null) {
buckets[index] = next;
} else {
previous.next = next;
}
_elementCount--;
_modificationCount =
(_modificationCount + 1) & _MODIFICATION_COUNT_MASK;
@ -609,11 +596,11 @@ class _LinkedHashMapValueIterable<V> extends IterableBase<V> {
}
abstract class _LinkedHashMapIterator<T> implements Iterator<T> {
final LinkedHashMap _map;
final _LinkedHashMap _map;
var _next;
T _current;
int _modificationCount;
_LinkedHashMapIterator(LinkedHashMap map)
_LinkedHashMapIterator(_LinkedHashMap map)
: _map = map,
_current = null,
_next = map._nextEntry,
@ -639,12 +626,12 @@ abstract class _LinkedHashMapIterator<T> implements Iterator<T> {
}
class _LinkedHashMapKeyIterator<K> extends _LinkedHashMapIterator<K> {
_LinkedHashMapKeyIterator(LinkedHashMap map) : super(map);
_LinkedHashMapKeyIterator(_LinkedHashMap map) : super(map);
K _getValue(_LinkedHashMapEntry entry) => entry.key;
}
class _LinkedHashMapValueIterator<V> extends _LinkedHashMapIterator<V> {
_LinkedHashMapValueIterator(LinkedHashMap map) : super(map);
_LinkedHashMapValueIterator(_LinkedHashMap map) : super(map);
V _getValue(_LinkedHashMapEntry entry) => entry.value;
}
@ -653,72 +640,153 @@ class _LinkedHashMapValueIterator<V> extends _LinkedHashMapIterator<V> {
* A hash-based map that iterates keys and values in key insertion order.
*/
patch class LinkedHashMap<K, V> {
static const int _INITIAL_CAPACITY = 8;
static const int _MODIFICATION_COUNT_MASK = 0x3fffffff;
int _elementCount = 0;
List<_HashMapEntry> _buckets = new List(_INITIAL_CAPACITY);
int _modificationCount = 0;
var _nextEntry;
var _previousEntry;
/* patch */ factory LinkedHashMap({ bool equals(K key1, K key2),
int hashCode(K key),
bool isValidKey(potentialKey) }) {
if (isValidKey == null) {
if (hashCode == null) {
if (equals == null) {
return new _LinkedHashMap<K, V>();
}
if (identical(identical, equals)) {
return new _LinkedIdentityHashMap<K, V>();
}
hashCode = _defaultHashCode;
} else if (equals == null) {
equals = _defaultEquals;
}
} else {
if (hashCode == null) {
hashCode = _defaultHashCode;
}
if (equals == null) {
equals = _defaultEquals;
}
}
return new _LinkedCustomHashMap<K, V>(equals, hashCode, isValidKey);
/* patch */ LinkedHashMap() {
_nextEntry = _previousEntry = this;
}
}
// Methods that are exactly the same in all three linked hash map variants.
abstract class _LinkedHashMapMixin<K, V> implements LinkedHashMap<K, V> {
var _nextEntry;
var _previousEntry;
/* patch */ int get length => _elementCount;
/* patch */ bool get isEmpty => _elementCount == 0;
/* patch */ bool get isNotEmpty => _elementCount != 0;
bool containsValue(Object value) {
int modificationCount = _modificationCount;
/* patch */ Iterable<K> get keys => new _LinkedHashMapKeyIterable<K>(this);
/* patch */ Iterable<V> get values => new _LinkedHashMapValueIterable<V>(this);
/* patch */ bool containsKey(Object key) {
int hashCode = key.hashCode;
List buckets = _buckets;
int index = hashCode & (buckets.length - 1);
_HashMapEntry entry = buckets[index];
while (entry != null) {
if (hashCode == entry.hashCode && entry.key == key) return true;
entry = entry.next;
}
return false;
}
/* patch */ bool containsValue(Object value) {
var cursor = _nextEntry;
int modificationCount = _modificationCount;
while (!identical(cursor, this)) {
_HashMapEntry entry = cursor;
if (entry.value == value) return true;
if (modificationCount != _modificationCount) {
throw new ConcurrentModificationError(this);
}
cursor = cursor._nextEntry;
}
return false;
}
void forEach(void action(K key, V value)) {
int modificationCount = _modificationCount;
/* patch */ V operator[](Object key) {
int hashCode = key.hashCode;
List buckets = _buckets;
int index = hashCode & (buckets.length - 1);
_HashMapEntry entry = buckets[index];
while (entry != null) {
if (hashCode == entry.hashCode && entry.key == key) {
return entry.value;
}
entry = entry.next;
}
return null;
}
/* patch */ void operator []=(K key, V value) {
int hashCode = key.hashCode;
List buckets = _buckets;
int length = buckets.length;
int index = hashCode & (length - 1);
_HashMapEntry entry = buckets[index];
while (entry != null) {
if (hashCode == entry.hashCode && entry.key == key) {
entry.value = value;
return;
}
entry = entry.next;
}
_addEntry(buckets, index, length, key, value, hashCode);
}
/* patch */ V putIfAbsent(K key, V ifAbsent()) {
int hashCode = key.hashCode;
List buckets = _buckets;
int length = buckets.length;
int index = hashCode & (length - 1);
_HashMapEntry entry = buckets[index];
while (entry != null) {
if (hashCode == entry.hashCode && entry.key == key) {
return entry.value;
}
entry = entry.next;
}
int stamp = _modificationCount;
V value = ifAbsent();
if (stamp == _modificationCount) {
_addEntry(buckets, index, length, key, value, hashCode);
} else {
this[key] = value;
}
return value;
}
/* patch */ void addAll(Map<K, V> other) {
other.forEach((K key, V value) {
this[key] = value;
});
}
/* patch */ void forEach(void action(K key, V value)) {
int stamp = _modificationCount;
var cursor = _nextEntry;
while (!identical(cursor, this)) {
_HashMapEntry entry = cursor;
action(entry.key, entry.value);
if (modificationCount != _modificationCount) {
if (stamp != _modificationCount) {
throw new ConcurrentModificationError(this);
}
cursor = cursor._nextEntry;
}
}
void clear() {
_nextEntry = _previousEntry = this;
/* patch */ V remove(Object key) {
int hashCode = key.hashCode;
List buckets = _buckets;
int index = hashCode & (buckets.length - 1);
_LinkedHashMapEntry entry = buckets[index];
_HashMapEntry previous = null;
while (entry != null) {
_HashMapEntry next = entry.next;
if (hashCode == entry.hashCode && entry.key == key) {
if (previous == null) {
buckets[index] = next;
} else {
previous.next = next;
}
entry._previousEntry._nextEntry = entry._nextEntry;
entry._nextEntry._previousEntry = entry._previousEntry;
entry._nextEntry = entry._previousEntry = null;
_elementCount--;
_modificationCount =
(_modificationCount + 1) & _MODIFICATION_COUNT_MASK;
return entry.value;
}
previous = entry;
entry = next;
}
return null;
}
/* patch */ void clear() {
_elementCount = 0;
_buckets = new List(_HashMap._INITIAL_CAPACITY);
_nextEntry = _previousEntry = this;
_buckets = new List(_INITIAL_CAPACITY);
_modificationCount = (_modificationCount + 1) & _MODIFICATION_COUNT_MASK;
}
@ -736,50 +804,26 @@ abstract class _LinkedHashMapMixin<K, V> implements LinkedHashMap<K, V> {
_modificationCount = (_modificationCount + 1) & _MODIFICATION_COUNT_MASK;
}
void _removeEntry(_LinkedHashMapEntry entry,
_HashMapEntry previousInBucket,
int bucketIndex) {
var previousInChain = entry._previousEntry;
var nextInChain = entry._nextEntry;
previousInChain._nextEntry = nextInChain;
nextInChain._previousEntry = previousInChain;
if (previousInBucket == null) {
_buckets[bucketIndex] = entry.next;
} else {
previousInBucket.next = entry.next;
void _resize() {
List oldBuckets = _buckets;
int oldLength = oldBuckets.length;
int newLength = oldLength << 1;
List newBuckets = new List(newLength);
for (int i = 0; i < oldLength; i++) {
_HashMapEntry entry = oldBuckets[i];
while (entry != null) {
_HashMapEntry next = entry.next;
int hashCode = entry.hashCode;
int index = hashCode & (newLength - 1);
entry.next = newBuckets[index];
newBuckets[index] = entry;
entry = next;
}
}
}
Iterable<K> get keys => new _LinkedHashMapKeyIterable<K>(this);
Iterable<V> get values => new _LinkedHashMapValueIterable<V>(this);
}
class _LinkedHashMap<K, V> extends _HashMap<K, V>
with _LinkedHashMapMixin<K, V> {
_LinkedHashMap() {
_nextEntry = _previousEntry = this;
_buckets = newBuckets;
}
}
class _LinkedIdentityHashMap<K, V> extends _IdentityHashMap<K, V>
with _LinkedHashMapMixin<K, V> {
_LinkedIdentityHashMap() {
_nextEntry = _previousEntry = this;
}
}
class _LinkedCustomHashMap<K, V> extends _CustomHashMap<K, V>
with _LinkedHashMapMixin<K, V> {
_LinkedCustomHashMap(bool equals(K key1, K key2),
int hashCode(K key),
bool isValidKey(potentialKey))
: super(equals, hashCode, isValidKey) {
_nextEntry = _previousEntry = this;
}
}
patch class LinkedHashSet<E> extends _HashSetBase<E> {
static const int _INITIAL_CAPACITY = 8;
_LinkedHashTable<E> _table;

View file

@ -6,34 +6,23 @@
import 'dart:_foreign_helper' show JS;
patch class HashMap<K, V> {
patch factory HashMap({ bool equals(K key1, K key2),
int hashCode(K key),
bool isValidKey(potentialKey) }) {
if (isValidKey == null) {
if (hashCode == null) {
if (equals == null) {
return new _HashMap<K, V>();
}
if (identical(identical, equals)) {
return new _IdentityHashMap<K, V>();
}
hashCode = _defaultHashCode;
} else if (equals == null) {
equals = _defaultEquals;
}
} else {
if (hashCode == null) {
hashCode = _defaultHashCode;
}
patch factory HashMap({ bool equals(K key1, K key2), int hashCode(K key) }) {
if (hashCode == null) {
if (equals == null) {
equals = _defaultEquals;
return new _HashMapImpl<K, V>();
}
if (identical(identical, equals)) {
return new _IdentityHashMap<K, V>();
}
hashCode = _defaultHashCode;
} else if (equals == null) {
equals = _defaultEquals;
}
return new _CustomHashMap<K, V>(equals, hashCode, isValidKey);
return new _CustomHashMap<K, V>(equals, hashCode);
}
}
class _HashMap<K, V> implements HashMap<K, V> {
class _HashMapImpl<K, V> implements HashMap<K, V> {
int _length = 0;
// The hash map contents are divided into three parts: one part for
@ -54,7 +43,7 @@ class _HashMap<K, V> implements HashMap<K, V> {
// guard against concurrent modifications.
List _keys;
_Hash();
_HashMapImpl();
int get length => _length;
bool get isEmpty => _length == 0;
@ -245,7 +234,7 @@ class _HashMap<K, V> implements HashMap<K, V> {
_setTableEntry(table, key, value);
}
V _removeHashTableEntry(var table, Object key) {
V _removeHashTableEntry(var table, K key) {
if (table != null && _hasTableEntry(table, key)) {
V value = _getTableEntry(table, key);
_deleteTableEntry(table, key);
@ -336,7 +325,7 @@ class _HashMap<K, V> implements HashMap<K, V> {
}
}
class _IdentityHashMap<K, V> extends _HashMap<K, V> {
class _IdentityHashMap<K, V> extends _HashMapImpl<K, V> {
int _findBucketIndex(var bucket, var key) {
if (bucket == null) return -1;
int length = JS('int', '#.length', bucket);
@ -347,27 +336,10 @@ class _IdentityHashMap<K, V> extends _HashMap<K, V> {
}
}
class _CustomHashMap<K, V> extends _HashMap<K, V> {
class _CustomHashMap<K, V> extends _HashMapImpl<K, V> {
final _Equality<K> _equals;
final _Hasher<K> _hashCode;
final _Predicate _validKey;
_CustomHashMap(this._equals, this._hashCode, bool validKey(potentialKey))
: _validKey = (validKey != null) ? validKey : ((v) => v is K);
V operator[](Object key) {
if (!_validKey(key)) return null;
return super[key];
}
bool containsKey(Object key) {
if (!_validKey(key)) return false;
return super.containsKey(key);
}
V remove(Object key) {
if (!_validKey(key)) return null;
return super.remove(key);
}
_CustomHashMap(this._equals, this._hashCode);
int _computeHashCode(var key) {
// We force the hash codes to be unsigned 30-bit integers to avoid
@ -444,34 +416,6 @@ class HashMapKeyIterator<E> implements Iterator<E> {
}
patch class LinkedHashMap<K, V> {
patch factory LinkedHashMap({ bool equals(K key1, K key2),
int hashCode(K key),
bool isValidKey(potentialKey) }) {
if (isValidKey == null) {
if (hashCode == null) {
if (equals == null) {
return new _LinkedHashMap<K, V>();
}
if (identical(identical, equals)) {
return new _LinkedIdentityHashMap<K, V>();
}
hashCode = _defaultHashCode;
} else if (equals == null) {
equals = _defaultEquals;
}
} else {
if (hashCode == null) {
hashCode = _defaultHashCode;
}
if (equals == null) {
equals = _defaultEquals;
}
}
return new _LinkedCustomHashMap<K, V>(equals, hashCode, isValidKey);
}
}
class _LinkedHashMap<K, V> implements LinkedHashMap<K, V> {
int _length = 0;
// The hash map contents are divided into three parts: one part for
@ -496,22 +440,22 @@ class _LinkedHashMap<K, V> implements LinkedHashMap<K, V> {
// iterated over.
int _modifications = 0;
_LinkedHash();
patch LinkedHashMap();
int get length => _length;
bool get isEmpty => _length == 0;
bool get isNotEmpty => !isEmpty;
patch int get length => _length;
patch bool get isEmpty => _length == 0;
patch bool get isNotEmpty => !isEmpty;
Iterable<K> get keys {
patch Iterable<K> get keys {
return new LinkedHashMapKeyIterable<K>(this);
}
Iterable<V> get values {
patch Iterable<V> get values {
return keys.map((each) => this[each]);
}
bool containsKey(Object key) {
patch bool containsKey(Object key) {
if (_isStringKey(key)) {
var strings = _strings;
if (strings == null) return false;
@ -530,17 +474,17 @@ class _LinkedHashMap<K, V> implements LinkedHashMap<K, V> {
}
}
bool containsValue(Object value) {
patch bool containsValue(Object value) {
return keys.any((each) => this[each] == value);
}
void addAll(Map<K, V> other) {
patch void addAll(Map<K, V> other) {
other.forEach((K key, V value) {
this[key] = value;
});
}
V operator[](Object key) {
patch V operator[](Object key) {
if (_isStringKey(key)) {
var strings = _strings;
if (strings == null) return null;
@ -562,7 +506,7 @@ class _LinkedHashMap<K, V> implements LinkedHashMap<K, V> {
}
}
void operator[]=(K key, V value) {
patch void operator[]=(K key, V value) {
if (_isStringKey(key)) {
var strings = _strings;
if (strings == null) _strings = strings = _newHashTable();
@ -592,14 +536,14 @@ class _LinkedHashMap<K, V> implements LinkedHashMap<K, V> {
}
}
V putIfAbsent(K key, V ifAbsent()) {
patch V putIfAbsent(K key, V ifAbsent()) {
if (containsKey(key)) return this[key];
V value = ifAbsent();
this[key] = value;
return value;
}
V remove(Object key) {
patch V remove(Object key) {
if (_isStringKey(key)) {
return _removeHashTableEntry(_strings, key);
} else if (_isNumericKey(key)) {
@ -620,7 +564,7 @@ class _LinkedHashMap<K, V> implements LinkedHashMap<K, V> {
}
}
void clear() {
patch void clear() {
if (_length > 0) {
_strings = _nums = _rest = _first = _last = null;
_length = 0;
@ -628,7 +572,7 @@ class _LinkedHashMap<K, V> implements LinkedHashMap<K, V> {
}
}
void forEach(void action(K key, V value)) {
patch void forEach(void action(K key, V value)) {
LinkedHashMapCell cell = _first;
int modifications = _modifications;
while (cell != null) {
@ -649,7 +593,7 @@ class _LinkedHashMap<K, V> implements LinkedHashMap<K, V> {
}
}
V _removeHashTableEntry(var table, Object key) {
V _removeHashTableEntry(var table, K key) {
if (table == null) return null;
LinkedHashMapCell cell = _getTableEntry(table, key);
if (cell == null) return null;
@ -711,7 +655,7 @@ class _LinkedHashMap<K, V> implements LinkedHashMap<K, V> {
return key is num && JS('bool', '(# & 0x3ffffff) === #', key, key);
}
int _computeHashCode(var key) {
static int _computeHashCode(var key) {
// We force the hash codes to be unsigned 30-bit integers to avoid
// issues with problematic keys like '__proto__'. Another option
// would be to throw an exception if the hash code isn't a number.
@ -731,12 +675,12 @@ class _LinkedHashMap<K, V> implements LinkedHashMap<K, V> {
JS('void', 'delete #[#]', table, key);
}
List _getBucket(var table, var key) {
static List _getBucket(var table, var key) {
var hash = _computeHashCode(key);
return JS('var', '#[#]', table, hash);
}
int _findBucketIndex(var bucket, var key) {
static int _findBucketIndex(var bucket, var key) {
if (bucket == null) return -1;
int length = JS('int', '#.length', bucket);
for (int i = 0; i < length; i++) {
@ -758,61 +702,6 @@ class _LinkedHashMap<K, V> implements LinkedHashMap<K, V> {
_deleteTableEntry(table, temporaryKey);
return table;
}
String toString() => Maps.mapToString(this);
}
class _LinkedIdentityHashMap<K, V> extends _LinkedHashMap<K, V> {
int _findBucketIndex(var bucket, var key) {
if (bucket == null) return -1;
int length = JS('int', '#.length', bucket);
for (int i = 0; i < length; i++) {
LinkedHashMapCell cell = JS('var', '#[#]', bucket, i);
if (identical(cell._key, key)) return i;
}
return -1;
}
}
class _LinkedCustomHashMap<K, V> extends _LinkedHashMap<K, V> {
final _Equality<K> _equals;
final _Hasher<K> _hashCode;
final _Predicate _validKey;
_LinkedCustomHashMap(this._equals, this._hashCode,
bool validKey(potentialKey))
: _validKey = (validKey != null) ? validKey : ((v) => v is K);
V operator[](Object key) {
if (!_validKey(key)) return null;
return super[key];
}
bool containsKey(Object key) {
if (!_validKey(key)) return false;
return super.containsKey(key);
}
V remove(Object key) {
if (!_validKey(key)) return null;
return super.remove(key);
}
int _computeHashCode(var key) {
// We force the hash codes to be unsigned 30-bit integers to avoid
// issues with problematic keys like '__proto__'. Another option
// would be to throw an exception if the hash code isn't a number.
return JS('int', '# & 0x3ffffff', _hashCode(key));
}
int _findBucketIndex(var bucket, var key) {
if (bucket == null) return -1;
int length = JS('int', '#.length', bucket);
for (int i = 0; i < length; i++) {
LinkedHashMapCell cell = JS('var', '#[#]', bucket, i);
if (_equals(cell._key, key)) return i;
}
return -1;
}
}
class LinkedHashMapCell {

View file

@ -41,14 +41,6 @@ abstract class HashMap<K, V> implements Map<K, V> {
* for keys in order to place them in the hash table. If it is omitted, the
* key's own [Object.hashCode] is used.
*
* If using methods like [operator[]], [remove] and [containsKey] together
* with a custom equality and hashcode, an extra `isValidKey` function
* can be supplied. This function is called before calling [equals] or
* [hashCode] with an argument that may not be a [K] instance, and if the
* call returns false, the key is assumed to not be in the set.
* The [isValidKey] function defaults to just testing if the object is a
* [K] instance.
*
* The used `equals` and `hashCode` method should always be consistent,
* so that if `equals(a, b)` then `hashCode(a) == hashCode(b)`. The hash
* of an object, or what it compares equal to, should not change while the
@ -58,9 +50,7 @@ abstract class HashMap<K, V> implements Map<K, V> {
* you also want to supply the other. The only common exception is to pass
* [identical] as the equality and use the default hash code.
*/
external factory HashMap({bool equals(K key1, K key2),
int hashCode(K key),
bool isValidKey(potentialKey)});
external factory HashMap({bool equals(K key1, K key2), int hashCode(K key)});
/**
* Creates a [HashMap] that contains all key value pairs of [other].

View file

@ -18,10 +18,8 @@ part of dart.collection;
*
* The map allows `null` as a key.
*/
abstract class LinkedHashMap<K, V> implements HashMap<K, V> {
external factory LinkedHashMap({ bool equals(K key1, K key2),
int hashCode(K key),
bool isValidKey(potentialKey) });
class LinkedHashMap<K, V> implements HashMap<K, V> {
external LinkedHashMap();
/**
* Creates a [LinkedHashMap] that contains all key value pairs of [other].
@ -66,4 +64,35 @@ abstract class LinkedHashMap<K, V> implements HashMap<K, V> {
Maps._fillMapWithIterables(map, keys, values);
return map;
}
external bool containsKey(Object key);
external bool containsValue(Object value);
external void addAll(Map<K, V> other);
external V operator [](Object key);
external void operator []=(K key, V value);
external V putIfAbsent(K key, V ifAbsent());
external V remove(Object key);
external void clear();
external void forEach(void action (K key, V value));
/** The keys of the map, in insertion order. */
external Iterable<K> get keys;
/** The values of the map, in the order of their corresponding [keys].*/
external Iterable<V> get values;
external int get length;
external bool get isEmpty;
external bool get isNotEmpty;
String toString() => Maps.mapToString(this);
}

View file

@ -4,8 +4,6 @@
part of dart.collection;
typedef bool _Predicate<T>(T value);
/**
* A node in a splay tree. It holds the sorting key and the left
* and right children in the tree.
@ -231,10 +229,6 @@ abstract class _SplayTree<K> {
}
}
class _TypeTest<T> {
bool test(v) => v is T;
}
/*
* A [Map] of objects that can be ordered relative to each other.
*
@ -244,31 +238,19 @@ class _TypeTest<T> {
* Keys of the map are compared using the `compare` function passed in
* the constructor. If that is omitted, the objects are assumed to be
* [Comparable], and are compared using their [Comparable.compareTo]
* method. Non-comparable objects (including `null`) will not work as keys
* in that case.
*
* To allow calling [operator[]], [remove] or [containsKey] with objects
* that are not supported by the `compare` function, an extra `isValidKey`
* predicate function can be supplied. This function is tested before
* using the `compare` function on an argument value that may not be a [K]
* value. If omitted, the `isValidKey` function defaults to testing if the
* value is a [K].
* method. This also means that `null` is *not* allowed as a key.
*/
class SplayTreeMap<K, V> extends _SplayTree<K> implements Map<K, V> {
Comparator<K> _comparator;
_Predicate _validKey;
SplayTreeMap([int compare(K key1, K key2), bool isValidKey(potentialKey)])
: _comparator = (compare == null) ? Comparable.compare : compare,
_validKey = (isValidKey != null) ? isValidKey : ((v) => v is K);
SplayTreeMap([int compare(K key1, K key2)])
: _comparator = (compare == null) ? Comparable.compare : compare;
/**
* Creates a [SplayTreeMap] that contains all key value pairs of [other].
*/
factory SplayTreeMap.from(Map<K, V> other,
[ int compare(K key1, K key2),
bool isValidKey(potentialKey)]) =>
new SplayTreeMap(compare, isValidKey)..addAll(other);
factory SplayTreeMap.from(Map<K, V> other, [int compare(K key1, K key2)]) =>
new SplayTreeMap(compare)..addAll(other);
/**
* Creates a [SplayTreeMap] where the keys and values are computed from the
@ -284,9 +266,8 @@ class SplayTreeMap<K, V> extends _SplayTree<K> implements Map<K, V> {
* identity function.
*/
factory SplayTreeMap.fromIterable(Iterable<K> iterable,
{K key(element), V value(element), int compare(K key1, K key2),
bool isValidKey(potentialKey) }) {
SplayTreeMap<K, V> map = new SplayTreeMap<K, V>(compare, isValidKey);
{K key(element), V value(element), int compare(K key1, K key2)}) {
SplayTreeMap<K, V> map = new SplayTreeMap<K, V>(compare);
Maps._fillMapWithMappedIterable(map, iterable, key, value);
return map;
}
@ -303,8 +284,8 @@ class SplayTreeMap<K, V> extends _SplayTree<K> implements Map<K, V> {
* It is an error if the two [Iterable]s don't have the same length.
*/
factory SplayTreeMap.fromIterables(Iterable<K> keys, Iterable<V> values,
[int compare(K key1, K key2), bool isValidKey(potentialKey)]) {
SplayTreeMap<K, V> map = new SplayTreeMap<K, V>(compare, isValidKey);
[int compare(K key1, K key2)]) {
SplayTreeMap<K, V> map = new SplayTreeMap<K, V>(compare);
Maps._fillMapWithIterables(map, keys, values);
return map;
}
@ -315,7 +296,7 @@ class SplayTreeMap<K, V> extends _SplayTree<K> implements Map<K, V> {
V operator [](Object key) {
if (key == null) throw new ArgumentError(key);
if (!_validKey(key)) return null;
if (key is! K) return null;
if (_root != null) {
int comp = _splay(key);
if (comp == 0) {
@ -327,7 +308,7 @@ class SplayTreeMap<K, V> extends _SplayTree<K> implements Map<K, V> {
}
V remove(Object key) {
if (!_validKey(key)) return null;
if (key is! K) return null;
_SplayTreeMapNode mapRoot = _remove(key);
if (mapRoot != null) return mapRoot.value;
return null;
@ -397,7 +378,7 @@ class SplayTreeMap<K, V> extends _SplayTree<K> implements Map<K, V> {
}
bool containsKey(Object key) {
return _validKey(key) && _splay(key) == 0;
return key is K && _splay(key) == 0;
}
bool containsValue(Object value) {

View file

@ -33,8 +33,6 @@ void main() {
testNumericKeys(new HashMap<num, String>(equals: identical));
testNumericKeys(new LinkedHashMap());
testNumericKeys(new LinkedHashMap<num, String>());
testNumericKeys(new LinkedHashMap(equals: identical));
testNumericKeys(new LinkedHashMap<num, String>(equals: identical));
testNaNKeys(new Map());
testNaNKeys(new Map<num, String>());
@ -46,40 +44,10 @@ void main() {
// NaN is not equal to NaN.
testIdentityMap(new HashMap(equals: identical));
testIdentityMap(new LinkedHashMap(equals: identical));
testCustomMap(new HashMap(equals: myEquals, hashCode: myHashCode,
isValidKey: (v) => v is Customer));
testCustomMap(new LinkedHashMap(equals: myEquals, hashCode: myHashCode,
isValidKey: (v) => v is Customer));
testCustomMap(new HashMap<Customer,dynamic>(equals: myEquals,
hashCode: myHashCode));
testCustomMap(new LinkedHashMap<Customer,dynamic>(equals: myEquals,
hashCode: myHashCode));
testCustomMap(new HashMap(equals: myEquals, hashCode: myHashCode));
testIterationOrder(new LinkedHashMap());
testIterationOrder(new LinkedHashMap(equals: identical));
testOtherKeys(new SplayTreeMap<int, int>());
testOtherKeys(new SplayTreeMap<int, int>((int a, int b) => a - b,
(v) => v is int));
testOtherKeys(new SplayTreeMap((int a, int b) => a - b,
(v) => v is int));
testOtherKeys(new HashMap<int, int>());
testOtherKeys(new HashMap<int, int>(equals: identical));
testOtherKeys(new HashMap<int, int>(hashCode: (v) => v.hashCode,
isValidKey: (v) => v is int));
testOtherKeys(new HashMap(equals: (int x, int y) => x == y,
hashCode: (int v) => v.hashCode,
isValidKey: (v) => v is int));
testOtherKeys(new LinkedHashMap<int, int>());
testOtherKeys(new LinkedHashMap<int, int>(equals: identical));
testOtherKeys(new LinkedHashMap<int, int>(hashCode: (v) => v.hashCode,
isValidKey: (v) => v is int));
testOtherKeys(new LinkedHashMap(equals: (int x, int y) => x == y,
hashCode: (int v) => v.hashCode,
isValidKey: (v) => v is int));
}
@ -674,26 +642,9 @@ class Customer {
int myHashCode(Customer c) => c.secondId;
bool myEquals(Customer a, Customer b) => a.secondId == b.secondId;
void testIterationOrder(Map map) {
testIterationOrder(Map map) {
var order = [0, 6, 4, 2, 7, 9, 7, 1, 2, 5, 3];
for (int i = 0; i < order.length; i++) map[order[i]] = i;
Expect.listEquals(map.keys.toList(), [0, 6, 4, 2, 7, 9, 1, 5, 3]);
Expect.listEquals(map.values.toList(), [0, 1, 2, 8, 6, 5, 7, 9, 10]);
}
void testOtherKeys(Map<int, int> map) {
// Test that non-int keys are allowed in containsKey/remove/lookup.
// Custom hash sets and tree sets must be constructed so they don't
// use the equality/comparator on incompatible objects.
// This should not throw in either checked or unchecked mode.
map[0] = 0;
map[1] = 1;
map[2] = 2;
Expect.isFalse(map.containsKey("not an int"));
Expect.isFalse(map.containsKey(1.5));
Expect.isNull(map.remove("not an int"));
Expect.isNull(map.remove(1.5));
Expect.isNull(map["not an int"]);
Expect.isNull(map[1.5]);
}

View file

@ -9,7 +9,7 @@ import "package:expect/expect.dart";
import 'dart:collection';
class Foo extends HashSet {
class Foo extends LinkedHashMap {
}
main() {

View file

@ -8,11 +8,11 @@
import "package:expect/expect.dart";
import 'dart:collection';
class Crash extends HashSet<String> {
class Crash extends LinkedHashMap<String,String> {
Crash(): super();
}
void main() {
Crash map = new Crash();
Expect.isTrue(map is HashSet);
Expect.isTrue(map is LinkedHashMap);
}