Merge branch 'v5-dev' of gitee.com:dromara/hutool into v5-dev

This commit is contained in:
Looly 2022-03-08 19:22:23 +08:00
commit 62fd367fc4
9 changed files with 551 additions and 234 deletions

View File

@ -9,6 +9,8 @@
* 【core 】 AnnotationUtil增加getAnnotationAlias方法pr#554@Gitee
* 【core 】 FileUtil.extName增加对tar.gz特殊处理issue#I4W5FS@Gitee
* 【crypto 】 增加XXTEA实现issue#I4WH2X@Gitee
* 【core 】 增加Table实现issue#2179@Github
*
### 🐞Bug修复
* 【core 】 修复ObjectUtil.hasNull传入null返回true的问题pr#555@Gitee
* 【core 】 修复NumberConverter对数字转换的问题issue#I4WPF4@Gitee

View File

@ -906,4 +906,19 @@ public class IterUtil {
// 当两个Iterable长度不一致时返回false
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();
}
}
}
}

View 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();
}
}

View File

@ -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;
}
}

View File

@ -13,7 +13,6 @@ import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
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() {
final Set<Map.Entry<K, V>> hashSet = new LinkedHashSet<>();
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;
}
@ -191,7 +190,7 @@ public class TableMap<K, V> implements Map<K, V>, Iterable<Map.Entry<K, V>>, Ser
@Override
public Map.Entry<K, V> next() {
return new Entry<>(keysIter.next(), valuesIter.next());
return new SimpleEntry<>(keysIter.next(), valuesIter.next());
}
@Override
@ -209,48 +208,4 @@ public class TableMap<K, V> implements Map<K, V>, Iterable<Map.Entry<K, V>>, Ser
", 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);
}
}
}

View 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;
}
}
}

View File

@ -1,68 +1,75 @@
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.TransIter;
import cn.hutool.core.util.ObjectUtil;
import com.sun.istack.internal.Nullable;
import cn.hutool.core.map.AbsEntry;
import cn.hutool.core.map.SimpleEntry;
import java.io.Serializable;
import java.util.AbstractCollection;
import java.util.AbstractMap;
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> {
/**
* 将行的键作为主键的{@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 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) {
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.supplier = null == columnMapSupplier ? HashMap::new : columnMapSupplier;
this.columnBuilder = null == columnMapBuilder ? HashMap::new : columnMapBuilder;
}
//endregion
@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());
}
}
return raw.computeIfAbsent(rowKey, (key) -> columnBuilder.build()).put(columnKey, value);
}
@Override
@ -89,184 +96,164 @@ public class RowKeyTable<R, C, V> implements Table<R, C, V> {
}
@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 {
public boolean containsColumn(C columnKey) {
if (columnKey == null) {
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) {
for (Map<C, V> map : raw.values()) {
if (null != map && map.containsKey(columnKey)) {
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
public int hashCode() {
return Objects.hash(rowKey, columnKey, value);
}
//region columnMap
@Override
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
public String toString() {
return "(" + rowKey + "," + columnKey + ")=" + value;
public Set<Entry<C, Map<R, V>>> entrySet() {
return new ColumnMapEntrySet();
}
}
private final Collection<V> values = new AbstractCollection<V>() {
@Override
public Iterator<V> iterator() {
return new TransIter<>(cellSet().iterator(), Cell::getValue);
}
private class ColumnMapEntrySet extends AbstractSet<Map.Entry<C, Map<R, V>>> {
private final Set<C> columnKeySet = columnKeySet();
@Override
public boolean contains(Object o) {
//noinspection unchecked
return containsValue((V) o);
}
@Override
public void clear() {
RowKeyTable.this.clear();
public Iterator<Map.Entry<C, Map<R, V>>> iterator() {
return new TransIter<>(columnKeySet.iterator(),
c -> new SimpleEntry<>(c, getColumn(c)));
}
@Override
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
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());
public Iterator<C> iterator() {
return new ColumnKeyIterator();
}
@Override
public int size() {
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
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());
public Set<Entry<R, V>> entrySet() {
return new EntrySet();
}
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
public void clear() {
RowKeyTable.this.clear();
}
private class EntrySetIterator extends ComputeIter<Entry<R, V>> {
final Iterator<Entry<R, Map<C, V>>> iterator = raw.entrySet().iterator();
@Override
public Iterator<Table.Cell<R, C, V>> iterator() {
return new CellIterator();
}
@Override
protected Entry<R, V> computeNext() {
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
public int size() {
return RowKeyTable.this.size();
@Override
public V getValue() {
return entry.getValue().get(columnKey);
}
@Override
public V setValue(V value) {
return entry.getValue().put(columnKey, value);
}
};
}
}
return null;
}
}
};
}
//endregion
}

View File

@ -170,7 +170,13 @@ public interface Table<R, C, V> extends Iterable<Table.Cell<R, C, V>> {
*
* @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());
}
}
}
/**
* 移除指定值

View File

@ -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));
}
}