java泛型和集合
问候,
介绍这周,我将写一些关于泛型的东西(那些有趣的尖括号)。 我需要
一个示例,并决定使用集合及其一些操作。 这周
本文讨论了一个集合类和对该集合的两个有趣的操作:
组合和集合分区。 首先对算法进行描述
给定,然后我们将对它们的通用实现有一些乐趣。
组合方式对于给定的集合S = [x,y,z],有八组代表所有可能
这三个不同元素的组合:
- []
- [X]
- [y]
- [z]
- [x,y]
- [x,z]
- [y,z]
- [x,y,z]
给定集合S,我们如何找到所有这些组合? 方法如下:假设
我们可以找到集合S'= [y,z]的所有组合,它们是:[],[y],[z]和[y,z]
当然,这些子集也是原始集合S的组合,但是
在它们中没有找到“ x”(当然不是,因为我省略了它)。
只需将元素“ x”添加到组成组合的所有子集中
集合[y,z]; 结果如下:
[x],[x,y],[x,z]和[x,y,z]
连同其他四个子集,我们构建了所有八个组合
的集合S = [x,y,z]
但是我们如何找到集合S'= [y,z]的所有组合? 我们应用类似的
在这里进行递归推理:假设我们可以找到集合S''= [z]的所有组合;
他们是[]和[c]; 它们是集合S'= [y,z]的所有组合的一部分,并且
我们在其中添加元素“ y”:[y],[y,z]。
这似乎是徒劳的,但这是我们找到集合S''= [z]的所有组合的方式:
我们找到空集的所有组合,即空集本身:[],
然后将元素[z]添加到所有组合(仅一个),即[z]。
对于具有n个元素的集合,有2 ^ n个组合。 这很容易看出:
空集(零元素)具有一种组合:空集本身。 如果
我们在集合中添加一个元素,最终得到的组合数量是原来的两倍:
原始组合加上具有该新元素的原始组合
添加到每个组合。 一组元素具有两个组合,一组
两个元素具有四个组合,等等。
上述的递归算法将全部生成。
隔断集S的分区是一个或多个分离的非空子集,并且并集
这些集合中的一个再次形成集合S。 对于集合S = [x,y,z],分区为:
- [x,y,z]
- [x],[y,z]
- [x,y],[z]
- [x,z],[y]
- [x],[y],[z]
我们如何找到集合的所有分区? 我们应用类似的递归推理:
假设我们可以找到集合S'= [y,z]的所有分区,即:
- [y,z]
- [y],[z]
首先,我们将集合[x]添加到集合S'的每个分区中:
- [x],[y,z]
- [x],[y],[z]
接下来,我们将元素“ x”一一添加到S的分区中的每个集合中:
- [x,y,z]
- [x,y],[z]
- [y],[x,z]
我们再次以相同的五个分区结束。 尝试查找的所有分区
给定集合S''= [z]的分区,集合S'= [y,z]。 一个分区
元素集本身就是单个元素集,因此集S''的所有分区为
集S''= [z]本身。
根据定义,空集的所有分区都是空集本身。
套装您可能已经注意到,本文是关于集合的。 Java编程
语言具有可用的Set实现。 我将使用HashSet
本文示例的实现。 我将改变行为
实现的一点点:我想要“ COW”集。 “ COW”是的首字母缩写
“写时复制”。 COW表示该集合在需要时会复制自身
改变 副本将被更改。 让我们弄脏双手。
这是我们(通用)集合的构造函数:
public class S<T> extends HashSet<T> {
private static final long serialVersionUID = -8875179328198507416L;
public S(Collection<T> collection) { super(collection); }
public S(T arg) { this.add(arg); }
public S(T ... args) { super(Arrays.asList(args)); }
...
我添加了serialVerionUID以保持Eclipse的状态。
这个班有三个
构造函数:采用Collection <T>作为其副本构造函数的一种形式
论据; 以单个元素T作为参数的构造函数,
一个接受(可能为空)T个论点列表的构造函数。
最后一个构造函数允许我们编写新的S <T>(),即根本没有任何元素
这导致空集。
我再次包括第二个构造函数,以保持Eclipse的正常运行。 我需要
套集如下:
S<S<T>> set= new S<S<T>>(new S<T>());
假设我没有包括第二个构造函数,那么Java编译器必须
推论给定最后一个构造函数,需要将类型为S <T>的通用数组
构造为将单个参数放入。类型S <T>不是类型T,因此
Eclipse警告自己的创造力。 如果我没有包括那一秒
构造函数,一切仍然会很好,但是我只是不喜欢警告。
最后一个构造函数只是将varargs数组转换为一个List(这是一个
集合),因此超类构造函数可以处理所有这些。
Set接口定义返回“ true”或“ false”的“ add()”和“ addAll()”方法
指示是否可以添加元素。 这同样适用于
'remove'和'removeAll'方法。 我不想要这种行为,我想要方法
这样做相同,但返回新集。 开始:
private S<T> unionHere(T arg) {
add(arg);
return this;
}
public S<T> union(T arg) {
return new S<T>(this).unionHere(arg);
}
public S<T> union(Collection<T> collection) {
S<T> copy= new S<T>(this);
copy.addAll(collection);
return copy;
}
public S<T> difference(T arg) {
S<T> copy= new S<T>(this);
copy.remove(arg);
return copy;
}
public S<T> difference(Collection<T> collection) {
S<T> copy= new S<T>(this);
copy.removeAll(collection);
return copy;
}
请注意,我将第一个方法设为私有:我不希望用户摆弄
设置本身; 如果他们想这样做,他们可以摆弄超类
更改集合本身的方法。
上面的公共方法仅复制该集合并将所需的操作应用于
而是复制并返回复制的集,而不返回布尔值。
请注意,所有方法都是通用的:它们并不关心T实际是什么类型。
到目前为止还不错,但是我还没有做很多事情。 以下是其他设置操作:
相交,差和对称差。 前几个例子
操作; A = [x,y,z],B = [y,z,t]
- 交集:[y,z]
- 差异:[x]
- 对称差:[x,t]
A和B的交集是在A和B中都出现的元素。
A和B的差异是在A中发生但不在B中发生的那些元素。
A和B的对称性差异是A或B bot中的元素不在两者中。
对称差加上两个集合的交集是那些集合的并集
两套。 集A和B的对称差是差的并集
和B的差和B和A的差。
这是源代码:
public S<T> difference(Collection<T> collection) {
S<T> copy= new S<T>(this);
copy.removeAll(collection);
return copy;
}
public S<T> symDifference(Collection<T> collection) {
return difference(collection).union(new S<T>(collection).difference(this));
}
public S<T> intersection(Collection<T> collection) {
S<T> copy= new S<T>(this);
copy.retainAll(collection);
return copy;
}
关于这些方法,除了它们首先使
集的副本,并将操作应用于集的副本。
组合方式“组合”方法的实现紧随描述
在以上段落中。 该方法返回一组集合。 集合是
原始组合的各个组合。
首先,该方法构造一个包含空集的集。 如果原来
set不为空,它将遍历每个元素,将其删除并生成所有
化简集的组合。 它将组合复制到“结果”集中
并在将当前元素再次添加到集合后再次复制它们。
这是代码:
public S<S<T>> combinations() {
S<S<T>> result= new S<S<T>>(new S<T>());
for (T current : this) {
S<S<T>> comb= difference(current).combinations();
result.addAll(comb);
for (S<T> set : comb)
result.unionHere(set.union(current));
}
return result;
}
我的方法本身返回修改后的集合,这非常方便
正确/错误值。 不是通用类型S <S <T >>,即类型T的一个或多个集合。
方法本身是在类S的泛型定义中定义的。
类型是T,所以S <S <T >>的(部分)专业化也是有效的。 其他
文字:例如,如果类型T的并集方法的定义有效,则
自动对通用类型S <T>有效。 上面的代码利用了
Java的通用行为。
我将在下一算法实现中更多地使用该功能:
隔断此方法使用相同的实现策略:递归调用自身
具有较小的集合,并使用返回值来收集
当前设置在一组分区中。 这里是:
public S<S<S<T>>> partitions() {
S<S<S<T>>> result= new S<S<S<T>>>();
if (size() < 2)
return result.unionHere(new S<S<T>>(this));
T current= iterator().next();
S<S<S<T>>> part= difference(current).partitions();
for (S<S<T>> set : part) {
result.unionHere(set.union(new S<T>(current)));
for (S<T> elem : set) {
S<S<T>> copy= set.difference(elem);
result.unionHere(copy.unionHere(elem.union(current)));
}
}
return result;
}
分区是一组集合,因此所有分区一起是一组集合
套 最后一组包含类型T的元素。
对于包含最多一个元素的集合,包含包含以下内容的集合的集合
返回(几乎)空集。 否则,如
应用上面的段落,并返回已填充的“结果”集。
如您所见,在类型为<T>的方法的通用定义中,类型为S <T>,
S <S <T >>甚至S <S <S <T >>>是自动定义的,因为S <T>,S <S <T >>
和S <S <S <T >>>本身也是纯泛型类型。
这样就可以找到相交,组合或其他任何东西
对于字符串集(例如S <String>),也对于字符串集集,
即S <S <String >>或您想要的集合中的任何元素。
例子完成所有这些编程后,我希望看到一些结果; 我测试了上面
像这样的方法:
public static void main(String[] args) {
S<String> s= new S<String>("x", "y", "z");
S<String> t= new S<String>("y", "z", "t");
System.out.println("union : "+s.union(t));
System.out.println("intersection : "+s.intersection(t));
System.out.println("difference : "+s.difference(t));
System.out.println("symDifference: "+s.symDifference(t));
System.out.println("combinations : "+s.combinations());
System.out.println("partitions : "+s.partitions());
}
这是(正确的)输出:
union : [t, z, y, x]
intersection : [z, y]
difference : [x]
symDifference: [t, x]
combinations : [[], [z, x], [z, y], [z], [y], [z, y, x], [x], [y, x]]
partitions : [[[z, x], [y]], [[z, y], [x]], [[z], [y], [x]], [[z, y, x]], [[z], [y, x]]]
结束语
本章介绍了一些泛型的设置杂耍和摆弄
Java的功能。 我确定您必须阅读这篇小文章,
充分了解通用功能和递归实现的时间
两种操作。
我希望下周见,
亲切的问候,
乔斯
翻译自: https://bytes.com/topic/java/insights/709126-generics-sets
java泛型和集合