mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
addTable
This commit is contained in:
parent
04dc6d2b73
commit
820db7fa32
@ -70,7 +70,7 @@ public class EqualsBuilder implements Builder<Boolean> {
|
||||
* Converters value pair into a register pair.
|
||||
* </p>
|
||||
*
|
||||
* @param lhs <code>this</code> object
|
||||
* @param lhs {@code this} object
|
||||
* @param rhs the other object
|
||||
* @return the pair
|
||||
*/
|
||||
@ -82,15 +82,15 @@ public class EqualsBuilder implements Builder<Boolean> {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns <code>true</code> if the registry contains the given object pair.
|
||||
* Returns {@code true} if the registry contains the given object pair.
|
||||
* Used by the reflection methods to avoid infinite loops.
|
||||
* Objects might be swapped therefore a check is needed if the object pair
|
||||
* is registered in given or swapped order.
|
||||
* </p>
|
||||
*
|
||||
* @param lhs <code>this</code> object to lookup in registry
|
||||
* @param lhs {@code this} object to lookup in registry
|
||||
* @param rhs the other object to lookup on registry
|
||||
* @return boolean <code>true</code> if the registry contains the given object.
|
||||
* @return boolean {@code true} if the registry contains the given object.
|
||||
* @since 3.0
|
||||
*/
|
||||
static boolean isRegistered(final Object lhs, final Object rhs) {
|
||||
@ -108,7 +108,7 @@ public class EqualsBuilder implements Builder<Boolean> {
|
||||
* Used by the reflection methods to avoid infinite loops.
|
||||
* </p>
|
||||
*
|
||||
* @param lhs <code>this</code> object to register
|
||||
* @param lhs {@code this} object to register
|
||||
* @param rhs the other object to register
|
||||
*/
|
||||
static void register(final Object lhs, final Object rhs) {
|
||||
@ -131,7 +131,7 @@ public class EqualsBuilder implements Builder<Boolean> {
|
||||
* <p>
|
||||
* Used by the reflection methods to avoid infinite loops.
|
||||
*
|
||||
* @param lhs <code>this</code> object to unregister
|
||||
* @param lhs {@code this} object to unregister
|
||||
* @param rhs the other object to unregister
|
||||
* @since 3.0
|
||||
*/
|
||||
@ -170,7 +170,7 @@ public class EqualsBuilder implements Builder<Boolean> {
|
||||
* @param lhs 此对象
|
||||
* @param rhs 另一个对象
|
||||
* @param excludeFields 排除的字段集合,如果有不参与计算equals的字段加入此集合即可
|
||||
* @return 两个对象是否equals,是返回<code>true</code>
|
||||
* @return 两个对象是否equals,是返回{@code true}
|
||||
*/
|
||||
public static boolean reflectionEquals(final Object lhs, final Object rhs, final Collection<String> excludeFields) {
|
||||
return reflectionEquals(lhs, rhs, ArrayUtil.toArray(excludeFields, String.class));
|
||||
@ -182,62 +182,62 @@ public class EqualsBuilder implements Builder<Boolean> {
|
||||
* @param lhs 此对象
|
||||
* @param rhs 另一个对象
|
||||
* @param excludeFields 排除的字段集合,如果有不参与计算equals的字段加入此集合即可
|
||||
* @return 两个对象是否equals,是返回<code>true</code>
|
||||
* @return 两个对象是否equals,是返回{@code true}
|
||||
*/
|
||||
public static boolean reflectionEquals(final Object lhs, final Object rhs, final String... excludeFields) {
|
||||
return reflectionEquals(lhs, rhs, false, null, excludeFields);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>This method uses reflection to determine if the two <code>Object</code>s
|
||||
* <p>This method uses reflection to determine if the two {@code Object}s
|
||||
* are equal.</p>
|
||||
*
|
||||
* <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private
|
||||
* <p>It uses {@code AccessibleObject.setAccessible} to gain access to private
|
||||
* fields. This means that it will throw a security exception if run under
|
||||
* a security manager, if the permissions are not set up correctly. It is also
|
||||
* not as efficient as testing explicitly. Non-primitive fields are compared using
|
||||
* <code>equals()</code>.</p>
|
||||
* {@code equals()}.</p>
|
||||
*
|
||||
* <p>If the TestTransients parameter is set to <code>true</code>, transient
|
||||
* <p>If the TestTransients parameter is set to {@code true}, transient
|
||||
* members will be tested, otherwise they are ignored, as they are likely
|
||||
* derived fields, and not part of the value of the <code>Object</code>.</p>
|
||||
* derived fields, and not part of the value of the {@code Object}.</p>
|
||||
*
|
||||
* <p>Static fields will not be tested. Superclass fields will be included.</p>
|
||||
*
|
||||
* @param lhs <code>this</code> object
|
||||
* @param lhs {@code this} object
|
||||
* @param rhs the other object
|
||||
* @param testTransients whether to include transient fields
|
||||
* @return <code>true</code> if the two Objects have tested equals.
|
||||
* @return {@code true} if the two Objects have tested equals.
|
||||
*/
|
||||
public static boolean reflectionEquals(final Object lhs, final Object rhs, final boolean testTransients) {
|
||||
return reflectionEquals(lhs, rhs, testTransients, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>This method uses reflection to determine if the two <code>Object</code>s
|
||||
* <p>This method uses reflection to determine if the two {@code Object}s
|
||||
* are equal.</p>
|
||||
*
|
||||
* <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private
|
||||
* <p>It uses {@code AccessibleObject.setAccessible} to gain access to private
|
||||
* fields. This means that it will throw a security exception if run under
|
||||
* a security manager, if the permissions are not set up correctly. It is also
|
||||
* not as efficient as testing explicitly. Non-primitive fields are compared using
|
||||
* <code>equals()</code>.</p>
|
||||
* {@code equals()}.</p>
|
||||
*
|
||||
* <p>If the testTransients parameter is set to <code>true</code>, transient
|
||||
* <p>If the testTransients parameter is set to {@code true}, transient
|
||||
* members will be tested, otherwise they are ignored, as they are likely
|
||||
* derived fields, and not part of the value of the <code>Object</code>.</p>
|
||||
* derived fields, and not part of the value of the {@code Object}.</p>
|
||||
*
|
||||
* <p>Static fields will not be included. Superclass fields will be appended
|
||||
* up to and including the specified superclass. A null superclass is treated
|
||||
* as java.lang.Object.</p>
|
||||
*
|
||||
* @param lhs <code>this</code> object
|
||||
* @param lhs {@code this} object
|
||||
* @param rhs the other object
|
||||
* @param testTransients whether to include transient fields
|
||||
* @param reflectUpToClass the superclass to reflect up to (inclusive),
|
||||
* may be <code>null</code>
|
||||
* may be {@code null}
|
||||
* @param excludeFields array of field names to exclude from testing
|
||||
* @return <code>true</code> if the two Objects have tested equals.
|
||||
* @return {@code true} if the two Objects have tested equals.
|
||||
* @since 2.0
|
||||
*/
|
||||
public static boolean reflectionEquals(final Object lhs, final Object rhs, final boolean testTransients, final Class<?> reflectUpToClass,
|
||||
@ -343,9 +343,9 @@ public class EqualsBuilder implements Builder<Boolean> {
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Adds the result of <code>super.equals()</code> to this builder.</p>
|
||||
* <p>Adds the result of {@code super.equals()} to this builder.</p>
|
||||
*
|
||||
* @param superEquals the result of calling <code>super.equals()</code>
|
||||
* @param superEquals the result of calling {@code super.equals()}
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
* @since 2.0
|
||||
*/
|
||||
@ -360,8 +360,8 @@ public class EqualsBuilder implements Builder<Boolean> {
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* <p>Test if two <code>Object</code>s are equal using their
|
||||
* <code>equals</code> method.</p>
|
||||
* <p>Test if two {@code Object}s are equal using their
|
||||
* {@code equals} method.</p>
|
||||
*
|
||||
* @param lhs the left hand object
|
||||
* @param rhs the right hand object
|
||||
@ -388,11 +388,11 @@ public class EqualsBuilder implements Builder<Boolean> {
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Test if two <code>long</code> s are equal.
|
||||
* Test if two {@code long} s are equal.
|
||||
* </p>
|
||||
*
|
||||
* @param lhs the left hand <code>long</code>
|
||||
* @param rhs the right hand <code>long</code>
|
||||
* @param lhs the left hand {@code long}
|
||||
* @param rhs the right hand {@code long}
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
*/
|
||||
public EqualsBuilder append(final long lhs, final long rhs) {
|
||||
@ -404,10 +404,10 @@ public class EqualsBuilder implements Builder<Boolean> {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Test if two <code>int</code>s are equal.</p>
|
||||
* <p>Test if two {@code int}s are equal.</p>
|
||||
*
|
||||
* @param lhs the left hand <code>int</code>
|
||||
* @param rhs the right hand <code>int</code>
|
||||
* @param lhs the left hand {@code int}
|
||||
* @param rhs the right hand {@code int}
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
*/
|
||||
public EqualsBuilder append(final int lhs, final int rhs) {
|
||||
@ -419,10 +419,10 @@ public class EqualsBuilder implements Builder<Boolean> {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Test if two <code>short</code>s are equal.</p>
|
||||
* <p>Test if two {@code short}s are equal.</p>
|
||||
*
|
||||
* @param lhs the left hand <code>short</code>
|
||||
* @param rhs the right hand <code>short</code>
|
||||
* @param lhs the left hand {@code short}
|
||||
* @param rhs the right hand {@code short}
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
*/
|
||||
public EqualsBuilder append(final short lhs, final short rhs) {
|
||||
@ -434,10 +434,10 @@ public class EqualsBuilder implements Builder<Boolean> {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Test if two <code>char</code>s are equal.</p>
|
||||
* <p>Test if two {@code char}s are equal.</p>
|
||||
*
|
||||
* @param lhs the left hand <code>char</code>
|
||||
* @param rhs the right hand <code>char</code>
|
||||
* @param lhs the left hand {@code char}
|
||||
* @param rhs the right hand {@code char}
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
*/
|
||||
public EqualsBuilder append(final char lhs, final char rhs) {
|
||||
@ -449,10 +449,10 @@ public class EqualsBuilder implements Builder<Boolean> {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Test if two <code>byte</code>s are equal.</p>
|
||||
* <p>Test if two {@code byte}s are equal.</p>
|
||||
*
|
||||
* @param lhs the left hand <code>byte</code>
|
||||
* @param rhs the right hand <code>byte</code>
|
||||
* @param lhs the left hand {@code byte}
|
||||
* @param rhs the right hand {@code byte}
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
*/
|
||||
public EqualsBuilder append(final byte lhs, final byte rhs) {
|
||||
@ -464,16 +464,16 @@ public class EqualsBuilder implements Builder<Boolean> {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Test if two <code>double</code>s are equal by testing that the
|
||||
* pattern of bits returned by <code>doubleToLong</code> are equal.</p>
|
||||
* <p>Test if two {@code double}s are equal by testing that the
|
||||
* pattern of bits returned by {@code doubleToLong} are equal.</p>
|
||||
*
|
||||
* <p>This handles NaNs, Infinities, and <code>-0.0</code>.</p>
|
||||
* <p>This handles NaNs, Infinities, and {@code -0.0}.</p>
|
||||
*
|
||||
* <p>It is compatible with the hash code generated by
|
||||
* <code>HashCodeBuilder</code>.</p>
|
||||
* {@code HashCodeBuilder}.</p>
|
||||
*
|
||||
* @param lhs the left hand <code>double</code>
|
||||
* @param rhs the right hand <code>double</code>
|
||||
* @param lhs the left hand {@code double}
|
||||
* @param rhs the right hand {@code double}
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
*/
|
||||
public EqualsBuilder append(final double lhs, final double rhs) {
|
||||
@ -484,16 +484,16 @@ public class EqualsBuilder implements Builder<Boolean> {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Test if two <code>float</code>s are equal byt testing that the
|
||||
* <p>Test if two {@code float}s are equal byt testing that the
|
||||
* pattern of bits returned by doubleToLong are equal.</p>
|
||||
*
|
||||
* <p>This handles NaNs, Infinities, and <code>-0.0</code>.</p>
|
||||
* <p>This handles NaNs, Infinities, and {@code -0.0}.</p>
|
||||
*
|
||||
* <p>It is compatible with the hash code generated by
|
||||
* <code>HashCodeBuilder</code>.</p>
|
||||
* {@code HashCodeBuilder}.</p>
|
||||
*
|
||||
* @param lhs the left hand <code>float</code>
|
||||
* @param rhs the right hand <code>float</code>
|
||||
* @param lhs the left hand {@code float}
|
||||
* @param rhs the right hand {@code float}
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
*/
|
||||
public EqualsBuilder append(final float lhs, final float rhs) {
|
||||
@ -504,10 +504,10 @@ public class EqualsBuilder implements Builder<Boolean> {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Test if two <code>booleans</code>s are equal.</p>
|
||||
* <p>Test if two {@code booleans}s are equal.</p>
|
||||
*
|
||||
* @param lhs the left hand <code>boolean</code>
|
||||
* @param rhs the right hand <code>boolean</code>
|
||||
* @param lhs the left hand {@code boolean}
|
||||
* @param rhs the right hand {@code boolean}
|
||||
* @return EqualsBuilder - used to chain calls.
|
||||
*/
|
||||
public EqualsBuilder append(final boolean lhs, final boolean rhs) {
|
||||
@ -519,7 +519,7 @@ public class EqualsBuilder implements Builder<Boolean> {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns <code>true</code> if the fields that have been checked
|
||||
* <p>Returns {@code true} if the fields that have been checked
|
||||
* are all equal.</p>
|
||||
*
|
||||
* @return boolean
|
||||
@ -529,11 +529,11 @@ public class EqualsBuilder implements Builder<Boolean> {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns <code>true</code> if the fields that have been checked
|
||||
* <p>Returns {@code true} if the fields that have been checked
|
||||
* are all equal.</p>
|
||||
*
|
||||
* @return <code>true</code> if all of the fields that have been checked
|
||||
* are equal, <code>false</code> otherwise.
|
||||
* @return {@code true} if all of the fields that have been checked
|
||||
* are equal, {@code false} otherwise.
|
||||
* @since 3.0
|
||||
*/
|
||||
@Override
|
||||
@ -542,7 +542,7 @@ public class EqualsBuilder implements Builder<Boolean> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <code>isEquals</code> value.
|
||||
* Sets the {@code isEquals} value.
|
||||
*
|
||||
* @param isEquals The value to set.
|
||||
* @return this
|
||||
|
@ -0,0 +1,272 @@
|
||||
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 com.sun.istack.internal.Nullable;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.AbstractCollection;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class RowKeyTable<R, C, V> implements Table<R, C, V> {
|
||||
|
||||
final Map<R, Map<C, V>> raw;
|
||||
final Supplier<? extends Map<C, V>> supplier;
|
||||
|
||||
public RowKeyTable(Map<R, Map<C, V>> raw) {
|
||||
this(raw, HashMap::new);
|
||||
}
|
||||
|
||||
public RowKeyTable(Map<R, Map<C, V>> raw, Supplier<? extends Map<C, V>> columnMapSupplier) {
|
||||
this.raw = raw;
|
||||
this.supplier = null == columnMapSupplier ? HashMap::new : columnMapSupplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<R, Map<C, V>> rowMap() {
|
||||
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
|
||||
public V put(R rowKey, C columnKey, V value) {
|
||||
return raw.computeIfAbsent(rowKey, (key) -> supplier.get()).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
|
||||
public V remove(R rowKey, C columnKey) {
|
||||
final Map<C, V> map = getRow(rowKey);
|
||||
if (null == map) {
|
||||
return null;
|
||||
}
|
||||
final V value = map.remove(columnKey);
|
||||
if (map.isEmpty()) {
|
||||
raw.remove(rowKey);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return raw.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.raw.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Cell<R, C, V>> iterator() {
|
||||
return new CellIterator();
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private final Collection<V> values = new 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() {
|
||||
RowKeyTable.this.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return RowKeyTable.this.size();
|
||||
}
|
||||
};
|
||||
|
||||
private final Set<Cell<R, C, V>> cellSet = new 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;
|
||||
RowKeyTable.this.remove(cell.getRowKey(), cell.getColumnKey());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
RowKeyTable.this.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Table.Cell<R, C, V>> iterator() {
|
||||
return new CellIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return RowKeyTable.this.size();
|
||||
}
|
||||
};
|
||||
}
|
253
hutool-core/src/main/java/cn/hutool/core/map/multi/Table.java
Normal file
253
hutool-core/src/main/java/cn/hutool/core/map/multi/Table.java
Normal file
@ -0,0 +1,253 @@
|
||||
package cn.hutool.core.map.multi;
|
||||
|
||||
import cn.hutool.core.lang.Opt;
|
||||
import cn.hutool.core.lang.func.Consumer3;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 表格数据结构定义<br>
|
||||
* 此结构类似于Guava的Table接口,使用两个键映射到一个值,类似于表格结构。
|
||||
*
|
||||
* @param <R> 行键类型
|
||||
* @param <C> 列键类型
|
||||
* @param <V> 值类型
|
||||
* @since 5.7.23
|
||||
*/
|
||||
public interface Table<R, C, V> extends Iterable<Table.Cell<R, C, V>> {
|
||||
|
||||
/**
|
||||
* 是否包含指定行列的映射<br>
|
||||
* 行和列任意一个不存在都会返回{@code false},如果行和列都存在,值为{@code null},也会返回{@code true}
|
||||
*
|
||||
* @param rowKey 行键
|
||||
* @param columnKey 列键
|
||||
* @return 是否包含映射
|
||||
*/
|
||||
default boolean contains(R rowKey, C columnKey) {
|
||||
return Opt.ofNullable(getRow(rowKey)).map((map) -> map.containsKey(columnKey)).get();
|
||||
}
|
||||
|
||||
//region Row
|
||||
|
||||
/**
|
||||
* 行是否存在
|
||||
*
|
||||
* @param rowKey 行键
|
||||
* @return 行是否存在
|
||||
*/
|
||||
default boolean containsRow(R rowKey) {
|
||||
return Opt.ofNullable(rowMap()).map((map) -> map.containsKey(rowKey)).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取行
|
||||
*
|
||||
* @param rowKey 行键
|
||||
* @return 行映射,返回的键为列键,值为表格的值
|
||||
*/
|
||||
default Map<C, V> getRow(R rowKey) {
|
||||
return Opt.ofNullable(rowMap()).map((map) -> map.get(rowKey)).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回所有行的key,行的key不可重复
|
||||
*
|
||||
* @return 行键
|
||||
*/
|
||||
default Set<R> rowKeySet() {
|
||||
return Opt.ofNullable(rowMap()).map(Map::keySet).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回行列对应的Map
|
||||
*
|
||||
* @return map,键为行键,值为列和值的对应map
|
||||
*/
|
||||
Map<R, Map<C, V>> rowMap();
|
||||
//endregion
|
||||
|
||||
//region Column
|
||||
|
||||
/**
|
||||
* 列是否存在
|
||||
*
|
||||
* @param columnKey 列键
|
||||
* @return 列是否存在
|
||||
*/
|
||||
default boolean containsColumn(C columnKey) {
|
||||
return Opt.ofNullable(columnMap()).map((map) -> map.containsKey(columnKey)).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取列
|
||||
*
|
||||
* @param columnKey 列键
|
||||
* @return 列映射,返回的键为行键,值为表格的值
|
||||
*/
|
||||
default Map<R, V> getColumn(C columnKey) {
|
||||
return Opt.ofNullable(columnMap()).map((map) -> map.get(columnKey)).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回所有列的key,列的key不可重复
|
||||
*
|
||||
* @return 列set
|
||||
*/
|
||||
default Set<C> columnKeySet() {
|
||||
return Opt.ofNullable(columnMap()).map(Map::keySet).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回列-行对应的map
|
||||
*
|
||||
* @return map,键为列键,值为行和值的对应map
|
||||
*/
|
||||
Map<C, Map<R, V>> columnMap();
|
||||
//endregion
|
||||
|
||||
//region value
|
||||
|
||||
/**
|
||||
* 指定值是否存在
|
||||
*
|
||||
* @param value 值
|
||||
* @return 值
|
||||
*/
|
||||
default boolean containsValue(V value){
|
||||
final Collection<Map<C, V>> rows = Opt.ofNullable(rowMap()).map(Map::values).get();
|
||||
if(null != rows){
|
||||
for (Map<C, V> row : rows) {
|
||||
if (row.containsValue(value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定值
|
||||
*
|
||||
* @param rowKey 行键
|
||||
* @param columnKey 列键
|
||||
* @return 值,如果值不存在,返回{@code null}
|
||||
*/
|
||||
default V get(R rowKey, C columnKey) {
|
||||
return Opt.ofNullable(getRow(rowKey)).map((map) -> map.get(columnKey)).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有行列值的集合
|
||||
*
|
||||
* @return 值的集合
|
||||
*/
|
||||
Collection<V> values();
|
||||
//endregion
|
||||
|
||||
/**
|
||||
* 所有单元格集合
|
||||
*
|
||||
* @return 单元格集合
|
||||
*/
|
||||
Set<Cell<R, C, V>> cellSet();
|
||||
|
||||
/**
|
||||
* 为表格指定行列赋值,如果不存在,创建之,存在则替换之,返回原值
|
||||
*
|
||||
* @param rowKey 行键
|
||||
* @param columnKey 列键
|
||||
* @param value 值
|
||||
* @return 原值,不存在返回{@code null}
|
||||
*/
|
||||
V put(R rowKey, C columnKey, V value);
|
||||
|
||||
/**
|
||||
* 批量加入
|
||||
*
|
||||
* @param table 其他table
|
||||
*/
|
||||
void putAll(Table<? extends R, ? extends C, ? extends V> table);
|
||||
|
||||
/**
|
||||
* 移除指定值
|
||||
*
|
||||
* @param rowKey 行键
|
||||
* @param columnKey 列键
|
||||
* @return 移除的值,如果值不存在,返回{@code null}
|
||||
*/
|
||||
V remove(R rowKey, C columnKey);
|
||||
|
||||
/**
|
||||
* 表格是否为空
|
||||
*
|
||||
* @return 是否为空
|
||||
*/
|
||||
boolean isEmpty();
|
||||
|
||||
/**
|
||||
* 表格大小,一般为单元格的个数
|
||||
*
|
||||
* @return 表格大小
|
||||
*/
|
||||
default int size(){
|
||||
final Map<R, Map<C, V>> rowMap = rowMap();
|
||||
if(MapUtil.isEmpty(rowMap)){
|
||||
return 0;
|
||||
}
|
||||
int size = 0;
|
||||
for (Map<C, V> map : rowMap.values()) {
|
||||
size += map.size();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空表格
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* 遍历表格的单元格,处理值
|
||||
*
|
||||
* @param consumer 单元格值处理器
|
||||
*/
|
||||
default void forEach(Consumer3<? super R, ? super C, ? super V> consumer) {
|
||||
for (Cell<R, C, V> cell : this) {
|
||||
consumer.accept(cell.getRowKey(), cell.getColumnKey(), cell.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 单元格,用于表示一个单元格的行、列和值
|
||||
*
|
||||
* @param <R> 行键类型
|
||||
* @param <C> 列键类型
|
||||
* @param <V> 值类型
|
||||
*/
|
||||
interface Cell<R, C, V> {
|
||||
/**
|
||||
* 获取行键
|
||||
*
|
||||
* @return 行键
|
||||
*/
|
||||
R getRowKey();
|
||||
|
||||
/**
|
||||
* 获取列键
|
||||
*
|
||||
* @return 列键
|
||||
*/
|
||||
C getColumnKey();
|
||||
|
||||
/**
|
||||
* 获取值
|
||||
*
|
||||
* @return 值
|
||||
*/
|
||||
V getValue();
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* 列表类型值的Map实现
|
||||
* 多参数类型的Map实现,包括集合类型值的Map和Table
|
||||
*
|
||||
* @author looly
|
||||
*
|
||||
*/
|
||||
package cn.hutool.core.map.multi;
|
||||
package cn.hutool.core.map.multi;
|
||||
|
Loading…
x
Reference in New Issue
Block a user