mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
add Table
This commit is contained in:
parent
820db7fa32
commit
01af68cd0d
@ -2,13 +2,15 @@
|
|||||||
# 🚀Changelog
|
# 🚀Changelog
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------
|
||||||
# 5.7.23 (2022-03-05)
|
# 5.7.23 (2022-03-08)
|
||||||
|
|
||||||
### 🐣新特性
|
### 🐣新特性
|
||||||
* 【http 】 HttpRequest.form采用TableMap方式(issue#I4W427@Gitee)
|
* 【http 】 HttpRequest.form采用TableMap方式(issue#I4W427@Gitee)
|
||||||
* 【core 】 AnnotationUtil增加getAnnotationAlias方法(pr#554@Gitee)
|
* 【core 】 AnnotationUtil增加getAnnotationAlias方法(pr#554@Gitee)
|
||||||
* 【core 】 FileUtil.extName增加对tar.gz特殊处理(issue#I4W5FS@Gitee)
|
* 【core 】 FileUtil.extName增加对tar.gz特殊处理(issue#I4W5FS@Gitee)
|
||||||
* 【crypto 】 增加XXTEA实现(issue#I4WH2X@Gitee)
|
* 【crypto 】 增加XXTEA实现(issue#I4WH2X@Gitee)
|
||||||
|
* 【core 】 增加Table实现(issue#2179@Github)
|
||||||
|
*
|
||||||
### 🐞Bug修复
|
### 🐞Bug修复
|
||||||
* 【core 】 修复ObjectUtil.hasNull传入null返回true的问题(pr#555@Gitee)
|
* 【core 】 修复ObjectUtil.hasNull传入null返回true的问题(pr#555@Gitee)
|
||||||
|
|
||||||
|
@ -906,4 +906,19 @@ public class IterUtil {
|
|||||||
// 当两个Iterable长度不一致时返回false
|
// 当两个Iterable长度不一致时返回false
|
||||||
return false == (it1.hasNext() || it2.hasNext());
|
return false == (it1.hasNext() || it2.hasNext());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空指定{@link Iterator},此方法遍历后调用{@link Iterator#remove()}移除每个元素
|
||||||
|
*
|
||||||
|
* @param iterator {@link Iterator}
|
||||||
|
* @since 5.7.23
|
||||||
|
*/
|
||||||
|
public static void clear(Iterator<?> iterator) {
|
||||||
|
if (null != iterator) {
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
iterator.next();
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
46
hutool-core/src/main/java/cn/hutool/core/map/AbsEntry.java
Normal file
46
hutool-core/src/main/java/cn/hutool/core/map/AbsEntry.java
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package cn.hutool.core.map;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抽象的{@link Map.Entry}实现,来自Guava<br>
|
||||||
|
* 实现了默认的{@link #equals(Object)}、{@link #hashCode()}、{@link #toString()}方法。<br>
|
||||||
|
* 默认{@link #setValue(Object)}抛出异常。
|
||||||
|
*
|
||||||
|
* @param <K> 键类型
|
||||||
|
* @param <V> 值类型
|
||||||
|
* @author Guava
|
||||||
|
* @since 5.7.23
|
||||||
|
*/
|
||||||
|
public abstract class AbsEntry<K, V> implements Map.Entry<K, V> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V setValue(V value) {
|
||||||
|
throw new UnsupportedOperationException("Entry is read only.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object object) {
|
||||||
|
if (object instanceof Map.Entry) {
|
||||||
|
final Map.Entry<?, ?> that = (Map.Entry<?, ?>) object;
|
||||||
|
return ObjectUtil.equals(this.getKey(), that.getKey())
|
||||||
|
&& ObjectUtil.equals(this.getValue(), that.getValue());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
//copy from 1.8 HashMap.Node
|
||||||
|
K k = getKey();
|
||||||
|
V v = getValue();
|
||||||
|
return ((k == null) ? 0 : k.hashCode()) ^ ((v == null) ? 0 : v.hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getKey() + "=" + getValue();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package cn.hutool.core.map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link java.util.Map.Entry}简单实现。<br>
|
||||||
|
* 键值对使用不可变字段表示。
|
||||||
|
*
|
||||||
|
* @param <K> 键类型
|
||||||
|
* @param <V> 值类型
|
||||||
|
* @author looly
|
||||||
|
* @since 5.7.23
|
||||||
|
*/
|
||||||
|
public class SimpleEntry<K, V> extends AbsEntry<K, V> {
|
||||||
|
|
||||||
|
private final K key;
|
||||||
|
private final V value;
|
||||||
|
|
||||||
|
public SimpleEntry(K key, V value) {
|
||||||
|
this.key = key;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public K getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,6 @@ import java.util.Iterator;
|
|||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -173,7 +172,7 @@ public class TableMap<K, V> implements Map<K, V>, Iterable<Map.Entry<K, V>>, Ser
|
|||||||
public Set<Map.Entry<K, V>> entrySet() {
|
public Set<Map.Entry<K, V>> entrySet() {
|
||||||
final Set<Map.Entry<K, V>> hashSet = new LinkedHashSet<>();
|
final Set<Map.Entry<K, V>> hashSet = new LinkedHashSet<>();
|
||||||
for (int i = 0; i < size(); i++) {
|
for (int i = 0; i < size(); i++) {
|
||||||
hashSet.add(new Entry<>(keys.get(i), values.get(i)));
|
hashSet.add(new SimpleEntry<>(keys.get(i), values.get(i)));
|
||||||
}
|
}
|
||||||
return hashSet;
|
return hashSet;
|
||||||
}
|
}
|
||||||
@ -191,7 +190,7 @@ public class TableMap<K, V> implements Map<K, V>, Iterable<Map.Entry<K, V>>, Ser
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map.Entry<K, V> next() {
|
public Map.Entry<K, V> next() {
|
||||||
return new Entry<>(keysIter.next(), valuesIter.next());
|
return new SimpleEntry<>(keysIter.next(), valuesIter.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -209,48 +208,4 @@ public class TableMap<K, V> implements Map<K, V>, Iterable<Map.Entry<K, V>>, Ser
|
|||||||
", values=" + values +
|
", values=" + values +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Entry<K, V> implements Map.Entry<K, V> {
|
|
||||||
|
|
||||||
private final K key;
|
|
||||||
private final V value;
|
|
||||||
|
|
||||||
public Entry(K key, V value) {
|
|
||||||
this.key = key;
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public K getKey() {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public V getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public V setValue(V value) {
|
|
||||||
throw new UnsupportedOperationException("setValue not supported.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final boolean equals(Object o) {
|
|
||||||
if (o == this)
|
|
||||||
return true;
|
|
||||||
if (o instanceof Map.Entry) {
|
|
||||||
Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
|
|
||||||
return Objects.equals(key, e.getKey()) &&
|
|
||||||
Objects.equals(value, e.getValue());
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
//copy from 1.8 HashMap.Node
|
|
||||||
return Objects.hashCode(key) ^ Objects.hashCode(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
236
hutool-core/src/main/java/cn/hutool/core/map/multi/AbsTable.java
Normal file
236
hutool-core/src/main/java/cn/hutool/core/map/multi/AbsTable.java
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
package cn.hutool.core.map.multi;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.IterUtil;
|
||||||
|
import cn.hutool.core.collection.TransIter;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.AbstractCollection;
|
||||||
|
import java.util.AbstractSet;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抽象{@link Table}接口实现<br>
|
||||||
|
* 默认实现了:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link #equals(Object)}</li>
|
||||||
|
* <li>{@link #hashCode()}</li>
|
||||||
|
* <li>{@link #toString()}</li>
|
||||||
|
* <li>{@link #values()}</li>
|
||||||
|
* <li>{@link #cellSet()}</li>
|
||||||
|
* <li>{@link #iterator()}</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param <R> 行类型
|
||||||
|
* @param <C> 列类型
|
||||||
|
* @param <V> 值类型
|
||||||
|
* @author Guava, Looly
|
||||||
|
* @since 5.7.23
|
||||||
|
*/
|
||||||
|
public abstract class AbsTable<R, C, V> implements Table<R, C, V> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == this) {
|
||||||
|
return true;
|
||||||
|
} else if (obj instanceof Table) {
|
||||||
|
final Table<?, ?, ?> that = (Table<?, ?, ?>) obj;
|
||||||
|
return this.cellSet().equals(that.cellSet());
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return cellSet().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return rowMap().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
//region values
|
||||||
|
@Override
|
||||||
|
public Collection<V> values() {
|
||||||
|
Collection<V> result = values;
|
||||||
|
return (result == null) ? values = new Values() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection<V> values;
|
||||||
|
private class Values extends AbstractCollection<V> {
|
||||||
|
@Override
|
||||||
|
public Iterator<V> iterator() {
|
||||||
|
return new TransIter<>(cellSet().iterator(), Cell::getValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
//noinspection unchecked
|
||||||
|
return containsValue((V) o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
AbsTable.this.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return AbsTable.this.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
//region cellSet
|
||||||
|
@Override
|
||||||
|
public Set<Cell<R, C, V>> cellSet() {
|
||||||
|
Set<Cell<R, C, V>> result = cellSet;
|
||||||
|
return (result == null) ? cellSet = new CellSet() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<Cell<R, C, V>> cellSet;
|
||||||
|
|
||||||
|
private class CellSet extends AbstractSet<Cell<R, C, V>> {
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
if (o instanceof Cell) {
|
||||||
|
@SuppressWarnings("unchecked") final Cell<R, C, V> cell = (Cell<R, C, V>) o;
|
||||||
|
Map<C, V> row = getRow(cell.getRowKey());
|
||||||
|
if (null != row) {
|
||||||
|
return ObjectUtil.equals(row.get(cell.getColumnKey()), cell.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object o) {
|
||||||
|
if (contains(o)) {
|
||||||
|
@SuppressWarnings("unchecked") final Cell<R, C, V> cell = (Cell<R, C, V>) o;
|
||||||
|
AbsTable.this.remove(cell.getRowKey(), cell.getColumnKey());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
AbsTable.this.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Table.Cell<R, C, V>> iterator() {
|
||||||
|
return new AbsTable<R, C, V>.CellIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return AbsTable.this.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
//region iterator
|
||||||
|
@Override
|
||||||
|
public Iterator<Cell<R, C, V>> iterator() {
|
||||||
|
return new CellIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于{@link Cell}的{@link Iterator}实现
|
||||||
|
*/
|
||||||
|
private class CellIterator implements Iterator<Cell<R, C, V>> {
|
||||||
|
final Iterator<Map.Entry<R, Map<C, V>>> rowIterator = rowMap().entrySet().iterator();
|
||||||
|
Map.Entry<R, Map<C, V>> rowEntry;
|
||||||
|
Iterator<Map.Entry<C, V>> columnIterator = IterUtil.empty();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return rowIterator.hasNext() || columnIterator.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cell<R, C, V> next() {
|
||||||
|
if (false == columnIterator.hasNext()) {
|
||||||
|
rowEntry = rowIterator.next();
|
||||||
|
columnIterator = rowEntry.getValue().entrySet().iterator();
|
||||||
|
}
|
||||||
|
final Map.Entry<C, V> columnEntry = columnIterator.next();
|
||||||
|
return new SimpleCell<>(rowEntry.getKey(), columnEntry.getKey(), columnEntry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
columnIterator.remove();
|
||||||
|
if (rowEntry.getValue().isEmpty()) {
|
||||||
|
rowIterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 简单{@link Cell} 实现
|
||||||
|
*
|
||||||
|
* @param <R> 行类型
|
||||||
|
* @param <C> 列类型
|
||||||
|
* @param <V> 值类型
|
||||||
|
*/
|
||||||
|
private static class SimpleCell<R, C, V> implements Cell<R, C, V>, Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private final R rowKey;
|
||||||
|
private final C columnKey;
|
||||||
|
private final V value;
|
||||||
|
|
||||||
|
SimpleCell(R rowKey, C columnKey, V value) {
|
||||||
|
this.rowKey = rowKey;
|
||||||
|
this.columnKey = columnKey;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R getRowKey() {
|
||||||
|
return rowKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public C getColumnKey() {
|
||||||
|
return columnKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == this) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj instanceof Cell) {
|
||||||
|
Cell<?, ?, ?> other = (Cell<?, ?, ?>) obj;
|
||||||
|
return ObjectUtil.equal(rowKey, other.getRowKey())
|
||||||
|
&& ObjectUtil.equal(columnKey, other.getColumnKey())
|
||||||
|
&& ObjectUtil.equal(value, other.getValue());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(rowKey, columnKey, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "(" + rowKey + "," + columnKey + ")=" + value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,68 +1,75 @@
|
|||||||
package cn.hutool.core.map.multi;
|
package cn.hutool.core.map.multi;
|
||||||
|
|
||||||
|
import cn.hutool.core.builder.Builder;
|
||||||
|
import cn.hutool.core.collection.ComputeIter;
|
||||||
import cn.hutool.core.collection.IterUtil;
|
import cn.hutool.core.collection.IterUtil;
|
||||||
import cn.hutool.core.collection.TransIter;
|
import cn.hutool.core.collection.TransIter;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.map.AbsEntry;
|
||||||
import com.sun.istack.internal.Nullable;
|
import cn.hutool.core.map.SimpleEntry;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.util.AbstractMap;
|
||||||
import java.util.AbstractCollection;
|
|
||||||
import java.util.AbstractSet;
|
import java.util.AbstractSet;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class RowKeyTable<R, C, V> implements Table<R, C, V> {
|
/**
|
||||||
|
* 将行的键作为主键的{@link Table}实现<br>
|
||||||
|
* 此结构为: 行=(列=值)
|
||||||
|
*
|
||||||
|
* @param <R> 行类型
|
||||||
|
* @param <C> 列类型
|
||||||
|
* @param <V> 值类型
|
||||||
|
* @author Guava, Looly
|
||||||
|
* @since 5.7.23
|
||||||
|
*/
|
||||||
|
public class RowKeyTable<R, C, V> extends AbsTable<R, C, V> {
|
||||||
|
|
||||||
final Map<R, Map<C, V>> raw;
|
final Map<R, Map<C, V>> raw;
|
||||||
final Supplier<? extends Map<C, V>> supplier;
|
/**
|
||||||
|
* 列的Map创建器,用于定义Table中Value对应Map类型
|
||||||
|
*/
|
||||||
|
final Builder<? extends Map<C, V>> columnBuilder;
|
||||||
|
|
||||||
|
//region 构造
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*/
|
||||||
|
public RowKeyTable() {
|
||||||
|
this(new HashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param raw 原始Map
|
||||||
|
*/
|
||||||
public RowKeyTable(Map<R, Map<C, V>> raw) {
|
public RowKeyTable(Map<R, Map<C, V>> raw) {
|
||||||
this(raw, HashMap::new);
|
this(raw, HashMap::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RowKeyTable(Map<R, Map<C, V>> raw, Supplier<? extends Map<C, V>> columnMapSupplier) {
|
/**
|
||||||
|
* 构造
|
||||||
|
*
|
||||||
|
* @param raw 原始Map
|
||||||
|
* @param columnMapBuilder 列的map创建器
|
||||||
|
*/
|
||||||
|
public RowKeyTable(Map<R, Map<C, V>> raw, Builder<? extends Map<C, V>> columnMapBuilder) {
|
||||||
this.raw = raw;
|
this.raw = raw;
|
||||||
this.supplier = null == columnMapSupplier ? HashMap::new : columnMapSupplier;
|
this.columnBuilder = null == columnMapBuilder ? HashMap::new : columnMapBuilder;
|
||||||
}
|
}
|
||||||
|
//endregion
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<R, Map<C, V>> rowMap() {
|
public Map<R, Map<C, V>> rowMap() {
|
||||||
return raw;
|
return raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<C, Map<R, V>> columnMap() {
|
|
||||||
// TODO 实现columnMap
|
|
||||||
throw new UnsupportedOperationException("TODO implement this method");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<V> values() {
|
|
||||||
return this.values;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<Cell<R, C, V>> cellSet() {
|
|
||||||
return this.cellSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public V put(R rowKey, C columnKey, V value) {
|
public V put(R rowKey, C columnKey, V value) {
|
||||||
return raw.computeIfAbsent(rowKey, (key) -> supplier.get()).put(columnKey, value);
|
return raw.computeIfAbsent(rowKey, (key) -> columnBuilder.build()).put(columnKey, value);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void putAll(Table<? extends R, ? extends C, ? extends V> table) {
|
|
||||||
if (null != table) {
|
|
||||||
for (Table.Cell<? extends R, ? extends C, ? extends V> cell : table.cellSet()) {
|
|
||||||
put(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -89,184 +96,164 @@ public class RowKeyTable<R, C, V> implements Table<R, C, V> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<Cell<R, C, V>> iterator() {
|
public boolean containsColumn(C columnKey) {
|
||||||
return new CellIterator();
|
if (columnKey == null) {
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(@Nullable Object obj) {
|
|
||||||
if (obj == this) {
|
|
||||||
return true;
|
|
||||||
} else if (obj instanceof Table) {
|
|
||||||
final Table<?, ?, ?> that = (Table<?, ?, ?>) obj;
|
|
||||||
return this.cellSet().equals(that.cellSet());
|
|
||||||
} else {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
for (Map<C, V> map : raw.values()) {
|
||||||
|
if (null != map && map.containsKey(columnKey)) {
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return cellSet().hashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return rowMap().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 基于{@link Cell}的{@link Iterator}实现
|
|
||||||
*/
|
|
||||||
private class CellIterator implements Iterator<Cell<R, C, V>> {
|
|
||||||
final Iterator<Map.Entry<R, Map<C, V>>> rowIterator = raw.entrySet().iterator();
|
|
||||||
Map.Entry<R, Map<C, V>> rowEntry;
|
|
||||||
Iterator<Map.Entry<C, V>> columnIterator = IterUtil.empty();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasNext() {
|
|
||||||
return rowIterator.hasNext() || columnIterator.hasNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Cell<R, C, V> next() {
|
|
||||||
if (false == columnIterator.hasNext()) {
|
|
||||||
rowEntry = rowIterator.next();
|
|
||||||
columnIterator = rowEntry.getValue().entrySet().iterator();
|
|
||||||
}
|
|
||||||
final Map.Entry<C, V> columnEntry = columnIterator.next();
|
|
||||||
return new SimpleCell<>(rowEntry.getKey(), columnEntry.getKey(), columnEntry.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void remove() {
|
|
||||||
columnIterator.remove();
|
|
||||||
if (rowEntry.getValue().isEmpty()) {
|
|
||||||
rowIterator.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 简单{@link Cell} 实现
|
|
||||||
*
|
|
||||||
* @param <R> 行类型
|
|
||||||
* @param <C> 列类型
|
|
||||||
* @param <V> 值类型
|
|
||||||
*/
|
|
||||||
private static class SimpleCell<R, C, V> implements Cell<R, C, V>, Serializable {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private final R rowKey;
|
|
||||||
private final C columnKey;
|
|
||||||
private final V value;
|
|
||||||
|
|
||||||
SimpleCell(@Nullable R rowKey, @Nullable C columnKey, @Nullable V value) {
|
|
||||||
this.rowKey = rowKey;
|
|
||||||
this.columnKey = columnKey;
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public R getRowKey() {
|
|
||||||
return rowKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public C getColumnKey() {
|
|
||||||
return columnKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public V getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (obj == this) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (obj instanceof Cell) {
|
|
||||||
Cell<?, ?, ?> other = (Cell<?, ?, ?>) obj;
|
|
||||||
return ObjectUtil.equal(rowKey, other.getRowKey())
|
|
||||||
&& ObjectUtil.equal(columnKey, other.getColumnKey())
|
|
||||||
&& ObjectUtil.equal(value, other.getValue());
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
//region columnMap
|
||||||
public int hashCode() {
|
@Override
|
||||||
return Objects.hash(rowKey, columnKey, value);
|
public Map<C, Map<R, V>> columnMap() {
|
||||||
}
|
Map<C, Map<R, V>> result = columnMap;
|
||||||
|
return (result == null) ? columnMap = new ColumnMap() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<C, Map<R, V>> columnMap;
|
||||||
|
|
||||||
|
private class ColumnMap extends AbstractMap<C, Map<R, V>> {
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public Set<Entry<C, Map<R, V>>> entrySet() {
|
||||||
return "(" + rowKey + "," + columnKey + ")=" + value;
|
return new ColumnMapEntrySet();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Collection<V> values = new AbstractCollection<V>() {
|
private class ColumnMapEntrySet extends AbstractSet<Map.Entry<C, Map<R, V>>> {
|
||||||
@Override
|
private final Set<C> columnKeySet = columnKeySet();
|
||||||
public Iterator<V> iterator() {
|
|
||||||
return new TransIter<>(cellSet().iterator(), Cell::getValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean contains(Object o) {
|
public Iterator<Map.Entry<C, Map<R, V>>> iterator() {
|
||||||
//noinspection unchecked
|
return new TransIter<>(columnKeySet.iterator(),
|
||||||
return containsValue((V) o);
|
c -> new SimpleEntry<>(c, getColumn(c)));
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clear() {
|
|
||||||
RowKeyTable.this.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() {
|
public int size() {
|
||||||
return RowKeyTable.this.size();
|
return columnKeySet.size();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
//region columnKeySet
|
||||||
|
@Override
|
||||||
|
public Set<C> columnKeySet() {
|
||||||
|
Set<C> result = columnKeySet;
|
||||||
|
return (result == null) ? columnKeySet = new ColumnKeySet() : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<C> columnKeySet;
|
||||||
|
|
||||||
|
private class ColumnKeySet extends AbstractSet<C> {
|
||||||
|
|
||||||
private final Set<Cell<R, C, V>> cellSet = new AbstractSet<Cell<R, C, V>>() {
|
|
||||||
@Override
|
@Override
|
||||||
public boolean contains(Object o) {
|
public Iterator<C> iterator() {
|
||||||
if (o instanceof Cell) {
|
return new ColumnKeyIterator();
|
||||||
@SuppressWarnings("unchecked") final
|
}
|
||||||
Cell<R, C, V> cell = (Cell<R, C, V>) o;
|
|
||||||
Map<C, V> row = getRow(cell.getRowKey());
|
@Override
|
||||||
if (null != row) {
|
public int size() {
|
||||||
return ObjectUtil.equals(row.get(cell.getColumnKey()), cell.getValue());
|
return IterUtil.size(iterator());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ColumnKeyIterator extends ComputeIter<C> {
|
||||||
|
final Map<C, V> seen = columnBuilder.build();
|
||||||
|
final Iterator<Map<C, V>> mapIterator = raw.values().iterator();
|
||||||
|
Iterator<Map.Entry<C, V>> entryIterator = IterUtil.empty();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected C computeNext() {
|
||||||
|
while (true) {
|
||||||
|
if (entryIterator.hasNext()) {
|
||||||
|
Map.Entry<C, V> entry = entryIterator.next();
|
||||||
|
if (false == seen.containsKey(entry.getKey())) {
|
||||||
|
seen.put(entry.getKey(), entry.getValue());
|
||||||
|
return entry.getKey();
|
||||||
|
}
|
||||||
|
} else if (mapIterator.hasNext()) {
|
||||||
|
entryIterator = mapIterator.next().entrySet().iterator();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
}
|
||||||
|
}
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
//region getColumn
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<R, V> getColumn(C columnKey) {
|
||||||
|
return new Column(columnKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Column extends AbstractMap<R, V> {
|
||||||
|
final C columnKey;
|
||||||
|
|
||||||
|
Column(C columnKey) {
|
||||||
|
this.columnKey = columnKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean remove(Object o) {
|
public Set<Entry<R, V>> entrySet() {
|
||||||
if (contains(o)) {
|
return new EntrySet();
|
||||||
@SuppressWarnings("unchecked")
|
}
|
||||||
final Cell<R, C, V> cell = (Cell<R, C, V>) o;
|
|
||||||
RowKeyTable.this.remove(cell.getRowKey(), cell.getColumnKey());
|
private class EntrySet extends AbstractSet<Map.Entry<R, V>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<Map.Entry<R, V>> iterator() {
|
||||||
|
return new EntrySetIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
int size = 0;
|
||||||
|
for (Map<C, V> map : raw.values()) {
|
||||||
|
if (map.containsKey(columnKey)) {
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return size;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class EntrySetIterator extends ComputeIter<Entry<R, V>> {
|
||||||
public void clear() {
|
final Iterator<Entry<R, Map<C, V>>> iterator = raw.entrySet().iterator();
|
||||||
RowKeyTable.this.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterator<Table.Cell<R, C, V>> iterator() {
|
protected Entry<R, V> computeNext() {
|
||||||
return new CellIterator();
|
while (iterator.hasNext()) {
|
||||||
}
|
final Entry<R, Map<C, V>> entry = iterator.next();
|
||||||
|
if (entry.getValue().containsKey(columnKey)) {
|
||||||
|
return new AbsEntry<R, V>() {
|
||||||
|
@Override
|
||||||
|
public R getKey() {
|
||||||
|
return entry.getKey();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int size() {
|
public V getValue() {
|
||||||
return RowKeyTable.this.size();
|
return entry.getValue().get(columnKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V setValue(V value) {
|
||||||
|
return entry.getValue().put(columnKey, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
//endregion
|
||||||
}
|
}
|
||||||
|
@ -170,7 +170,13 @@ public interface Table<R, C, V> extends Iterable<Table.Cell<R, C, V>> {
|
|||||||
*
|
*
|
||||||
* @param table 其他table
|
* @param table 其他table
|
||||||
*/
|
*/
|
||||||
void putAll(Table<? extends R, ? extends C, ? extends V> table);
|
default void putAll(Table<? extends R, ? extends C, ? extends V> table){
|
||||||
|
if (null != table) {
|
||||||
|
for (Table.Cell<? extends R, ? extends C, ? extends V> cell : table.cellSet()) {
|
||||||
|
put(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 移除指定值
|
* 移除指定值
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
package cn.hutool.core.map;
|
||||||
|
|
||||||
|
import cn.hutool.core.map.multi.RowKeyTable;
|
||||||
|
import cn.hutool.core.map.multi.Table;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class RowKeyTableTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void putGetTest(){
|
||||||
|
final Table<Integer, Integer, Integer> table = new RowKeyTable<>();
|
||||||
|
table.put(1, 2, 3);
|
||||||
|
table.put(1, 6, 4);
|
||||||
|
|
||||||
|
Assert.assertEquals(new Integer(3), table.get(1, 2));
|
||||||
|
Assert.assertNull(table.get(1, 3));
|
||||||
|
|
||||||
|
//判断row和column确定的二维点是否存在
|
||||||
|
Assert.assertTrue(table.contains(1, 2));
|
||||||
|
Assert.assertFalse(table.contains(1, 3));
|
||||||
|
|
||||||
|
//判断列
|
||||||
|
Assert.assertTrue(table.containsColumn(2));
|
||||||
|
Assert.assertFalse(table.containsColumn(3));
|
||||||
|
|
||||||
|
// 判断行
|
||||||
|
Assert.assertTrue(table.containsRow(1));
|
||||||
|
Assert.assertFalse(table.containsRow(2));
|
||||||
|
|
||||||
|
|
||||||
|
// 获取列
|
||||||
|
Map<Integer, Integer> column = table.getColumn(6);
|
||||||
|
Assert.assertEquals(1, column.size());
|
||||||
|
Assert.assertEquals(new Integer(4), column.get(1));
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user