@Override
	public Set<Entry<Pair<T, I>, V>> entrySet() {
		return new AbstractSet<Entry<Pair<T, I>, V>>() {
			@Override
			public boolean add(Entry<Pair<T, I>, V> e) {
				V res = PairMap.this.put(e.getKey(), e.getValue());
				return res != e.getValue();
			}

			@Override
			public void clear() {
				PairMap.this.clear();
			}

			@Override
			public boolean contains(Object o) {
				return o != null
						&& o instanceof Entry<?, ?>
						&& PairMap.this.containsKey(((Entry<?, ?>) o).getKey())
						&& PairMap.this.containsValue(((Entry<?, ?>) o).getValue());
			}

			@Override
			public boolean isEmpty() {
				return keys.isEmpty();
			}

			@Override
			public Iterator<Entry<Pair<T, I>, V>> iterator() {
				return new Iterator<Entry<Pair<T, I>, V>>() {
					final Iterator<Pair<T, I>> keyItr = keys.iterator();
					int valueIndex = -1;

					@Override
					public boolean hasNext() {
						return keyItr.hasNext();
					}

					@Override
					public Entry<Pair<T, I>, V> next() {
						final Pair<T, I> key = keyItr.next();
						valueIndex++;

						return new Entry<Pair<T, I>, V>() {
							@Override
							public Pair<T, I> getKey() {
								return key;
							}

							@Override
							public V getValue() {
								return values.get(valueIndex);
							}

							@Override
							public V setValue(V value) {
								return values.set(valueIndex, value);
							}
							
							@Override
							public String toString() {
								return "{" + getKey() + "=" + getValue() + "}";
							}
						};
					}

					@Override
					public void remove() {
						throw new UnsupportedOperationException();
					}
				};
			}

			@Override
			public boolean remove(Object o) {
				throw new UnsupportedOperationException();
			}

			@Override
			public int size() {
				return keys.size();
			}
		};
	}