Java集合框架:深入理解List与Set及其实现类

在Java集合框架中,List和Set是两个最常用的接口,它们各自有着独特的特点和适用场景。本文将详细介绍List和Set的区别,以及HashSet、LinkedHashSet和TreeSet这三个Set接口的主要实现类的特点、使用方法和适用场景。

一、List和Set集合的区别

1. 有序性

  • List:保证按插入顺序排序,元素的有序性是按照添加的先后顺序确定的
  • Set:存储和取出顺序不一致(除LinkedHashSet外)

2. 唯一性

  • List:元素可以重复,允许存储相同的元素
  • Set:元素唯一,不允许存储重复的元素

3. 获取元素方式

  • List:可以通过索引直接操作元素,提供了基于位置的访问方法
  • Set:不能根据索引获取元素,只能通过迭代器或增强for循环遍历

二、HashSet集合

1. HashSet集合的特点

  • HashSet底层使用的是HashMap实现
  • 不能保证元素的顺序,元素是无序的
  • 不能有重复的元素
  • 集合元素值允许为null
  • 线程不安全
  • 性能在Set实现类中通常是最好的

2. HashSet常用方法

① 添加元素:add(Object o)
HashSet<String> set = new HashSet<String>();
set.add("青城");
set.add("博雅");
System.out.println(set); // 输出顺序不确定
② 获取元素数量:size()

System.out.println(set.size()); // 输出元素个数

③ 删除元素:remove(Object o)

System.out.println(set.remove("博雅")); // 删除成功返回true

④ 判断集合是否为空:isEmpty()

System.out.println(set.isEmpty()); // 集合为空返回true

⑤ 清空集合:clear()

set.clear(); // 移除所有元素

⑥ 使用迭代器遍历:iterator()
Iterator<String> ite = set.iterator();
while(ite.hasNext()) {
    System.out.println(ite.next());
}
⑦ 判断是否包含元素:contains(Object o)

System.out.println(set.contains("青城")); // 包含返回true

⑧ 使用增强for循环遍历

for (String name : set) {

System.out.println(name);

}

三、LinkedHashSet集合

1. LinkedHashSet集合的特点

  • 元素有序且唯一
  • 链表保证元素有序(插入顺序)
  • 哈希表保证元素唯一
  • 线程不安全
  • 性能略低于HashSet,但高于TreeSet

2. LinkedHashSet的基本使用

① 创建LinkedHashSet
// 创建一个空的LinkedHashSet
LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();

// 创建具有初始容量的LinkedHashSet
LinkedHashSet<Integer> linkedHashSetWithCapacity = new LinkedHashSet<>(10);

// 创建具有初始容量和负载因子的LinkedHashSet
LinkedHashSet<Double> linkedHashSetWithLoadFactor = new LinkedHashSet<>(10, 0.75f);
② 添加元素
linkedHashSet.add("Apple");
linkedHashSet.add("Banana");
linkedHashSet.add("Cherry");
linkedHashSet.add("Apple"); // 重复元素,不会添加
System.out.println(linkedHashSet); // 输出: [Apple, Banana, Cherry] 保持插入顺序
③ 删除元素
linkedHashSet.remove("Banana");
System.out.println(linkedHashSet); // 输出: [Apple, Cherry]

linkedHashSet.clear(); // 清空所有元素
④ 检查元素是否存在
boolean containsApple = linkedHashSet.contains("Apple");
System.out.println("Contains Apple: " + containsApple);
⑤ 获取集合大小
int size = linkedHashSet.size();
System.out.println("Size: " + size);
⑥ 遍历LinkedHashSet
// 使用for-each循环遍历
for (String fruit : linkedHashSet) {
    System.out.println(fruit);
}

// 使用Iterator遍历
Iterator<String> iterator = linkedHashSet.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}
⑦ 转换为数组
String[] array = linkedHashSet.toArray(new String[0]);
for (String s : array) {
    System.out.println(s);
}
⑧ 其他常用方法
// 检查是否为空
boolean isEmpty = linkedHashSet.isEmpty();

// 保留与指定集合相同的元素
LinkedHashSet<String> fruits = new LinkedHashSet<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Cherry");

LinkedHashSet<String> toRetain = new LinkedHashSet<>();
toRetain.add("Apple");
toRetain.add("Cherry");

fruits.retainAll(toRetain); // 只保留Apple和Cherry

// 删除与指定集合相同的元素
fruits.removeAll(toRetain);

四、TreeSet集合

1. TreeSet集合的特点

  • TreeSet内部使用的是TreeMap,基于红黑树实现
  • 元素:TreeSet会对插入的数据排序,所以输入顺序和输出顺序不一致
  • 插入数据内部有两种排序方法:自然排序(默认)和定制排序
  • 值不能为null
  • 值唯一
  • 线程不安全
  • 性能在三者中通常是最低的

2. TreeSet的基本使用

① 插入是按自然顺序排序的
TreeSet ts = new TreeSet();
ts.add("agg");
ts.add("abcd");
ts.add("ffas");
Iterator it = ts.iterator();
while(it.hasNext()) {
    System.out.println(it.next());
}
// 输出:按照字典序排序
// abcd
// agg
// ffas
② 插入自定义对象需要实现Comparable接口
class Person implements Comparable {
    String name;
    int age;
    
    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public int compareTo(Object o) {
        Person p = (Person) o;
        // 先对姓名字典序比较,如果相同,比较年龄
        if(this.name.compareTo(p.name) != 0) {
            return this.name.compareTo(p.name);
        } else {
            if(this.age > p.age) return 1;
            else if(this.age < p.age) return -1;
        }
        return 0;
    }
}

public class Test {
    public static void main(String args[]) {
        TreeSet ts = new TreeSet();
        ts.add(new Person("agg", 21));
        ts.add(new Person("abcd", 12));
        ts.add(new Person("ffas", 8));
        ts.add(new Person("agg", 12));
        Iterator it = ts.iterator();
        while(it.hasNext()) {
            Person p = (Person) it.next();
            System.out.println(p.name + ":" + p.age);
        }
    }
}
// 输出:
// abcd:12
// agg:12
// agg:21
// ffas:8

五、HashSet、LinkedHashSet、TreeSet的使用场景

1. HashSet

  • 适用场景:在大多数情况下,HashSet是Set接口的首选实现。它的性能基本上比LinkedHashSet和TreeSet要好,特别是添加和查询操作,这也是使用最多的两个操作。
  • 特点:不关心元素的顺序,只关心元素是否存在。

2. LinkedHashSet

  • 适用场景:只有当要求插入顺序和取出顺序一致的时候才使用LinkedHashSet。LinkedHashSet的查询性能稍慢于HashSet,但它可以维持元素的添加顺序。
  • 特点:需要保持插入顺序,但又需要Set的唯一性特性。

3. TreeSet

  • 适用场景:只有在需要对元素进行排序时使用。当需要按照自然顺序或者自定义顺序对元素进行排序时,TreeSet是最佳选择。
  • 特点:元素有序,但性能相对较低。

总结

在Java集合框架中,List和Set各有其独特的用途和特点。List关注的是元素的索引和顺序,允许重复元素;而Set关注的是元素的唯一性,不关心元素的顺序(除LinkedHashSet外)。
对于Set的实现类:
  • 如果不需要保持任何顺序,优先使用HashSet
  • 如果需要保持插入顺序,使用LinkedHashSet
  • 如果需要元素按照自然顺序或自定义顺序排序,使用TreeSet
在实际开发中,根据具体需求选择合适的集合类非常重要,这不仅能提高代码的效率,也能使代码更加清晰易懂。理解这些集合类的底层实现原理和特点,有助于我们做出更加合理的选择。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值