目录
Interface Set<E>
观察API文档我们发现:
public interface Set<E>
extends Collection<E>不包含重复元素的集合。 更正式地,集合不包含一对元素
e1
和e2
,使得e1.equals(e2)
,并且最多一个空元素。 正如其名称所暗示的那样,这个接口模拟了数学集抽象。Set接口除了继承自Collection接口的所有构造函数的合同以及add,equals和hashCode方法的合同外 , 还 增加了其他规定。 其他继承方法的声明也包括在这里以方便。 (伴随这些声明的规范已经量身定做Set接口,但它们不包含任何附加的规定。)
构造函数的额外规定并不奇怪,所有构造函数都必须创建一个不包含重复元素的集合(如上所定义)。
注意:如果可变对象用作设置元素,则必须非常小心。 如果对象的值以影响equals比较的方式更改,而对象是集合中的元素, 则不指定集合的行为。 这种禁止的一个特殊情况是,一个集合不允许将其本身作为一个元素。
一些集合实现对它们可能包含的元素有限制。 例如,一些实现禁止空元素,有些实现对元素的类型有限制。 尝试添加不合格元素会引发未经检查的异常,通常为NullPointerException或ClassCastException 。 尝试查询不合格元素的存在可能会引发异常,或者可能只是返回false; 一些实现将展现出前者的行为,一些实现将展现出后者。 更一般来说,尝试对不符合条件的元素的操作,其完成不会导致不合格元素插入到集合中,可能会导致异常,或者可能会成功执行该选项。 此异常在此接口的规范中标记为“可选”。
此接口是成员Java Collections Framework 。
Set接口
一个不包含重复元素的 collection,就是元素唯一且元素无序(存储和取出不一致)的集合。
存储字符串并遍历,参考代码:
/*
存储字符串并遍历
*/
import java.util.HashSet;
public class SetDemo {
public static void main(String[] args) {
//定义字符串集合
HashSet<String> arr = new HashSet<>();
//添加元素到集合
arr.add("hello");
arr.add("world");
arr.add("java");
arr.add("bigdata");
arr.add("hadoop");
arr.add("hello");
arr.add("hello");
arr.add("java");
arr.add("spark");
arr.add("flink");
arr.add("world");
arr.add("hadoop");
for (String s : arr) {
System.out.println(s);
}
}
}
输出结果:
flink
world
java
bigdata
spark
hello
hadoop
HashSet
查看API文档我们知道:
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, Serializable此类实现Set接口,由哈希表(实际为HashMap实例)支持。 对集合的迭代次序不作任何保证; 特别是,它不能保证订单在一段时间内保持不变。 这个类允许null元素。
这个类提供了基本操作(add,remove,contains和size)固定的时间性能,假定哈希函数将分散的桶中正确的元素。 迭代此集合需要与HashSet实例的大小(元素数量)和后台HashMap实例(桶数)的“容量”的总和成比例的时间。 因此,如果迭代性能很重要,不要将初始容量设置得太高(或负载因子太低)是非常重要的。
请注意,此实现不同步。 如果多个线程并发访问哈希集,并且至少有一个线程修改该集合,那么它必须在外部进行同步。 这通常通过在自然地封装集合的一些对象上进行同步来实现。 如果没有这样的对象存在,那么该集合应该使用Collections.synchronizedSet方法“包装”。 这最好在创建时完成,以防止对该集合的意外不同步访问:
Set s = Collections.synchronizedSet(new HashSet(...));该类iterator方法返回的迭代器是故障快速的 :如果集合在迭代器创建之后的任何时间被修改,除了通过迭代器自己的remove方法之外,迭代器会抛出一个ConcurrentModificationException 。 因此,面对并发修改,迭代器将快速而干净地失败,而不是在未来未确定的时间冒着任意的非确定性行为。
请注意,迭代器的故障快速行为无法保证,因为一般来说,在不同步并发修改的情况下,无法做出任何硬性保证。 失败快速迭代器尽力投入ConcurrentModificationException 。 因此,编写依赖于此异常的程序的正确性将是错误的:迭代器的故障快速行为应仅用于检测错误。
通过观察API文档我们得出结论:
- 类允许为null
- 不保证set的迭代顺序
- 底层数据结构是哈希表(元素是链表的数组)
- 哈希表依赖于哈希值存储
- 添加功能底层依赖两个方法:
int hashCode()
boolean equals(Object obj)
存储自定义对象并遍历,参考代码:
创建Student2对象:
import java.util.Objects;
public class Student2 {
private String name;
private int age;
public Student2() {
}
public Student2(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student2{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student2 student2 = (Student2) o;
return age == student2.age &&
Objects.equals(name, student2.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
创建HashSetDemo测试类:
/*
存储自定义对象并遍历
*/
import java.util.HashSet;
public class HashSetDemo {
public static void main(String[] args) {
//创建集合对象
HashSet<Student2> hashSet = new HashSet<>();
//创建学生对象
Student2 s1 = new Student2("刘德华", 45);
Student2 s2 = new Student2("刘德华", 45);
Student2 s3 = new Student2("郭富城", 50);
Student2 s4 = new Student2("张学友", 60);
//将学生对象添加到集合中
hashSet.add(s1);
hashSet.add(s2);
hashSet.add(s3);
hashSet.add(s4);
//使用增强for遍历集合
for (Student2 s : hashSet){
System.out.println(s);
}
}
}
输出结果:
Student2{name='郭富城', age=50}
Student2{name='张学友', age=60}
Student2{name='刘德华', age=45}
LinkedHashSet
查看API文档我们知道:
public class LinkedHashSet<E> extends HashSet<E> implements Set<E>, Cloneable, Serializable哈希表和链表实现了Set接口,具有可预测的迭代次序。 这种实现不同于HashSet,它维持于所有条目的运行双向链表。 该链表定义了迭代排序,它是将元素插入集合(插入顺序 ) 的顺序 。 请注意,如果一个元件被重新插入到组插入顺序不受影响 。 (元件e重新插入一组s如果当s.contains(e)将返回true之前立即调用s.add(e)被调用。)
此实现可以让客户从提供的指定,通常杂乱无章的排序HashSet ,而不会导致与其相关的成本增加TreeSet 。 它可以用于生成与原始文件具有相同顺序的集合的副本,而不管原始集的实现:
void foo(Set s) { Set copy = new LinkedHashSet(s); ... }如果模块在输入上进行设置,复制它,并且稍后返回其顺序由该副本确定的结果,则此技术特别有用。 (客户一般都喜欢以相同的顺序返回事情。)
该类提供了所有可选的Set操作,并允许null元素。 像HashSet,它提供了基本操作(add,contains和remove)稳定的性能,假定散列函数散桶中适当的元件。 性能可能略低于HashSet ,由于维护链表的额外费用,但有一个例外:LinkedHashSet的迭代需要与集合的大小成比例的时间,无论其容量如何。 HashSet的迭代可能更昂贵,需要与其容量成比例的时间。
链接哈希集具有影响其性能的两个参数: 初始容量和负载因子 。 它们的定义精确到HashSet 。 但是请注意,该惩罚为初始容量选择非常高的值是该类比HashSet不太严重的,因为迭代次数对于这个类是由容量不受影响。
请注意,此实现不同步。 如果多个线程同时访问链接的散列集,并且至少有一个线程修改该集合,那么它必须在外部进行同步。 这通常通过在自然地封装集合的一些对象上进行同步来实现。 如果没有这样的对象存在,则应该使用Collections.synchronizedSet方法“包装”。 这最好在创建时完成,以防止对该集合的意外不同步访问:
Set s = Collections.synchronizedSet(new LinkedHashSet(...));该类iterator方法返回的迭代器是故障快速的 :如果在创建迭代器之后的任何时间对该集合进行了修改,除了通过迭代器自己的remove方法之外,迭代器将会抛出一个ConcurrentModificationException 。 因此,面对并发修改,迭代器将快速而干净地失败,而不是在未来未确定的时间冒着任意的非确定性行为。
请注意,迭代器的故障快速行为无法保证,因为一般来说,在不同步并发修改的情况下,无法做出任何硬性保证。 失败快速迭代器尽力投入ConcurrentModificationException 。 因此,编写依赖于此异常的程序的正确性将是错误的:迭代器的故障快速行为应仅用于检测错误。
通过观察API文档我们得出结论:
- 元素有序唯一
- 由链表保证元素有序
- 由哈希表保证元素唯一
参考代码:
import java.util.LinkedHashSet;
import java.util.TreeSet;
public class LinkedHashSetDemo {
public static void main(String[] args) {
//创建LinkedHashSet集合对象
LinkedHashSet<String> arr = new LinkedHashSet<>();
//创建TreeSet集合对象
TreeSet<String> strings = new TreeSet<>();
//添加元素到LinkedHashSet集合
arr.add("hello");
arr.add("world");
arr.add("java");
arr.add("bigdata");
arr.add("hadoop");
arr.add("hello");
arr.add("hello");
arr.add("java");
arr.add("spark");
arr.add("flink");
arr.add("world");
arr.add("hadoop");
//添加元素到TreeSet集合
strings.add("hello");
strings.add("world");
strings.add("java");
strings.add("bigdata");
strings.add("hadoop");
strings.add("hello");
strings.add("hello");
strings.add("java");
strings.add("spark");
strings.add("flink");
strings.add("world");
strings.add("hadoop");
System.out.println("遍历LinkedHashSet集合:");
for (String s : arr){
System.out.println(s);
}
System.out.println("=========================");
System.out.println("遍历TreeSet集合:");
for (String s1 : strings) {
System.out.println(s1);
}
}
}
输出结果:
遍历LinkedHashSet集合:
hello
world
java
bigdata
hadoop
spark
flink
=========================
遍历TreeSet集合:
bigdata
flink
hadoop
hello
java
spark
world
通过观察代码和输出结果我们得出结论:
- LinkedHashSet底层数据结构是哈希表和双向链表
- 哈希表保证了元素唯一
- 链表保证了元素的有序(存储和取出顺序一致)
通过输出结果我们发现,结果去重了,而且输出的结果顺序与添加的顺序不一样,这是为什么呢?
我们查看原码:
public interface Set<E> extends Collection<E>{
}
public class HashSet<E> extends AbstractSet<E> implements Set<E>{
private static final Object PRESENT = new Object();
private transient HashMap<E,Object> map;
public boolean add(E e) {
//E -- String
//e -- "hello"
return map.put(e, PRESENT)==null;
}
}
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>{
public V put(K key, V value) {
//key -- "hello"
//value -- new Object()
return putVal(hash(key), key, value, false, true);
}
//简单理解为调用元素类的hashCode()方法计算哈希值
static final int hash(Object key) { //"hello"
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
//理解为哈希表存储的是一个一个的结点数组
Node<K,V>[] tab;
Node<K,V> p;
int n, i;
//判断哈希表是否已经初始化完毕,如果没有初始化,就在这里初始化
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//根据元素对象计算好的哈希值再进行一次与运算,计算出的是该元素存储在哈希表中的位置
//如果该元素的位置是null,说明该位置没有元素,可以进行存储
//就创建新的结点,存储元素
//分析到这一步我们得出第一个结论,存储的位置与元素类中的hashCode()有关。
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
//如果该元素的位置不是null,说明这个位置上已经有元素了,可以确定是哈希值是一样的
//但是呢,我们并不能确定两个值是不是一样的
Node<K,V> e; K k;
//先将存入元素的哈希值与该位置中元素的哈希值做比较
//如果哈希值都不一样,继续走判断instanceof()
//如果哈希值都一样,会调用元素的equals(k)方法进行比较
//如果equals(k)方法进行比较结果是false,继续向下执行,最终会将元素插入到集合中或者不插入
//如果equals(k)方法进行比较结果是true,表示元素的哈希值和内容都一样,表示元素重复了
//就覆盖,从现象上来看,其实就是不赋值
//说到这里我们已经知道了add()方法和hashCode()以及equals()方法有关
//会不会去重取决于元素类型有没有重写hashCode()以及equals()方法
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
}
TreeSet
通过查看API文档我们知道:
public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, SerializableA NavigableSet实现基于TreeMap 。 的元件使用其有序natural ordering ,或由Comparator集合创建时提供,这取决于所使用的构造方法。
此实现提供了基本的操作(保证的log(n)时间成本
add
,remove
和contains
)。需要注意的是由一组(无论是否提供了明确的比较器)保持的顺序必须与equals一致 ,如果它是要正确实现
Set
接口。 (参见Comparable
或Comparator
为一致的精确定义与equals)。这是因为该Set
接口在来定义equals
的操作,但一个TreeSet
例如使用其执行所有元件比较compareTo
(或compare
)方法,于是两个通过该方法认为相等的元素从集合的角度来看是相等的。 集合的行为是明确定义的,即使其排序与equals不一致; 它只是没有遵守Set
界面的总体合同。请注意,此实现不同步。 如果多个线程并发访问树,并且至少有一个线程修改该集合,则必须在外部进行同步。 这通常通过在自然地封装集合的一些对象上进行同步来实现。 如果没有这样的对象存在,那么该集合应该使用Collections.synchronizedSortedSet方法“包装”。 这最好在创建时完成,以防止对该集合的意外不同步访问:
SortedSet s = Collections.synchronizedSortedSet(new TreeSet(...));该类
iterator
方法返回的迭代器是故障快速的 :如果在迭代器创建之后的任何时间对该集合进行了修改,除了通过迭代器自己的remove
方法之外,迭代器将抛出一个ConcurrentModificationException 。 因此,面对并发修改,迭代器将快速而干净地失败,而不是在未来未确定的时间冒着任意的非确定性行为。请注意,迭代器的故障快速行为无法保证,因为一般来说,在不同步并发修改的情况下,无法做出任何硬性保证。 失败快速迭代器尽力投入
ConcurrentModificationException
。 因此,编写依赖于此异常的程序的正确性将是错误的:迭代器的故障快速行为应仅用于检测错误。
通过观察API文档我们得出结论:
- 使用元素的自然顺序对元素进行排序
- 或者根据创建Set时提供的Comparator进行排序
- 具体取决于使用的构造方法
- 底层数据结构是红黑树(红黑树是一种自平衡的二叉树)
参考代码:
import java.util.TreeSet;
public class TreeSetDemo1 {
public static void main(String[] args) {
//创建集合对象
TreeSet<Integer> ts = new TreeSet<>();
//添加元素到集合中
ts.add(20);
ts.add(18);
ts.add(23);
ts.add(24);
ts.add(66);
ts.add(12);
ts.add(18);
ts.add(20);
ts.add(23);
ts.add(2);
//使用增强for循环遍历集合
for (Integer t : ts) {
System.out.println(t);
}
}
}
输出结果:
2
12
18
20
23
24
66
通过观察代码和运行结果我们得出结论:TreeSet元素唯一,元素顺序可以按照某种规则进行排序:自然排序、比较器排序。
那去重又是为什么呢?我们查看原码:
public abstract class AbstractCollection<E> implements Collection<E>{}
public abstract class AbstractSet<E> extends AbstractCollection<E> implements Set<E>{}
public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializable{
private transient NavigableMap<E,Object> m;
private static final Object PRESENT = new Object();
public TreeSet() {
this(new TreeMap<E,Object>());
}
TreeSet(NavigableMap<E,Object> m) { //NavigableMap<E,Object> m = new TreeMap<E,Object>()
this.m = m;
}
public boolean add(E e) {
//E -- Integer
//e -- 20
return m.put(e, PRESENT)==null;
}
}
public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>{
private transient Entry<K,V> root;
public TreeMap() {
comparator = null;
}
public V put(K key, V value) {
//key -- 18
//value -- new Object()
Entry<K,V> t = root;
//判断根有没有元素,如果没有,为当前元素值创建一个结点,当作树的根结点
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp; //0
Entry<K,V> parent; //null
// split comparator and comparable paths
Comparator<? super K> cpr = comparator; // cpr = null;
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}else {
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
}
TreeSet存储对学生对象遍历
参考代码1:
创建Student3类:
public class Student3 {
private String name;
private int age;
//创建无参的构造方法
public Student3() {
}
//创建有参的构造方法
public Student3(String name, int age) {
this.name = name;
this.age = age;
}
//创建getXxx()和setXxx()方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//重写toString方法
@Override
public String
toString() {
return "Struden3{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
创建TreeSeDemo2对象:
import java.util.TreeSet;
public class TreeSetDemo2 {
public static void main(String[] args) {
//创建集合对象
TreeSet<Object> ts = new TreeSet<>();
//创建学生集合对象
Student3 s1 = new Student3("张三", 24);
Student3 s2 = new Student3("李四", 32);
Student3 s3 = new Student3("王二", 53);
Student3 s4 = new Student3("麻子", 14);
Student3 s5 = new Student3("靓仔", 23);
Student3 s6 = new Student3("华仔", 67);
Student3 s7 = new Student3("老街", 35);
//将学生添加到集合中
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
//使用增强for循环遍历集合
for (Object t : ts) {
System.out.println(t);
}
}
}
输出结果:
输出结果报错了,这是为什么呢?我们查看原码:
public abstract class AbstractCollection<E> implements Collection<E>{}
public abstract class AbstractSet<E> extends AbstractCollection<E> implements Set<E>{}
public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializable{
private transient NavigableMap<E,Object> m;
private static final Object PRESENT = new Object();
public TreeSet() {
this(new TreeMap<E,Object>());
}
TreeSet(NavigableMap<E,Object> m) { //NavigableMap<E,Object> m = new TreeMap<E,Object>()
this.m = m;
}
public boolean add(E e) {
//E -- Integer
//e -- 20
return m.put(e, PRESENT)==null;
}
}
public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>{
private transient Entry<K,V> root;
public TreeMap() {
comparator = null;
}
public V put(K key, V value) {
//key -- 18
//value -- new Object()
Entry<K,V> t = root;
//判断根有没有元素,如果没有,为当前元素值创建一个结点,当作树的根结点
if (t == null) {
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp; //0
Entry<K,V> parent; //null
// split comparator and comparable paths
Comparator<? super K> cpr = comparator; // cpr = null;
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}else {
if (key == null)
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}
}
通过查看原码我们得知:由于我们这里创建TreeSet对象调用 的是无参构造方法,所以走的是自然排序,而底层原码有一步向下转型:
Comparable<? super K> k = (Comparable<? super K>) key;
原因是我们Student3类没有实现Comparable接口,无法向下转型,所以报错了。
那么实现一下Student3的Comparable接口:
我们再运行一下:
return 0;
return 1;
return -1;
通过不同的返回值我们发现:其实这里的返回值是根据我们的TreeSet的规则进行升序或降序的,比如我们想在去重的前提下,按照年龄进行排序:
returen this.age = o.age;
但题目的要求是年龄一样,姓名不一定不一样
通过运行结果我们发现姓名一样,年龄一样,只保留一开始添加到集合中的学生对象。(达到了题目的要求)
完整代码如下:
Student3:
public class Student3 implements Comparable<Student3>{
private String name;
private int age;
//创建无参的构造方法
public Student3() {
}
//创建有参的构造方法
public Student3(String name, int age) {
this.name = name;
this.age = age;
}
//创建getXxx()和setXxx()方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//重写toString方法
@Override
public String
toString() {
return "Struden3{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student3 o) {
//返回第一个学生对象
// return 0;
//升序
// return 1;
//降序
// return -1;
//年龄去重
// return this.age - o.age;
//姓名年龄都去重
int i = this.age - o.age;
int i2 = i == 0 ? this.name.compareTo(o.name) : i;
return i2;
}
}
TreeSetDemo2:
import java.util.TreeSet;
public class TreeSetDemo2 {
public static void main(String[] args) {
//创建集合对象
TreeSet<Object> ts = new TreeSet<>();
//创建学生集合对象
Student3 s1 = new Student3("张三", 24);
Student3 s2 = new Student3("李四", 32);
Student3 s3 = new Student3("王二", 53);
Student3 s4 = new Student3("麻子", 14);
Student3 s5 = new Student3("靓仔", 23);
Student3 s55 = new Student3("靓仔", 23);
Student3 s555 = new Student3("靓仔", 18);
Student3 s6 = new Student3("华仔", 67);
Student3 s7 = new Student3("老街", 35);
//将学生添加到集合中
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
ts.add(s6);
ts.add(s7);
ts.add(s55);
//使用增强for循环遍历集合
for (Object t : ts) {
System.out.println(t);
}
}
}
输出结果:
Struden3{name='麻子', age=14}
Struden3{name='靓仔', age=23}
Struden3{name='张三', age=24}
Struden3{name='李四', age=32}
Struden3{name='老街', age=35}
Struden3{name='王二', age=53}
Struden3{name='华仔', age=67}
🔶到底啦!给靓仔一个关注吧!🔶