Java基础集合框架学习(下)

简介: Java基础集合框架学习(下)

Dog必须改写equals方法

在Java中,当你希望对自定义类的对象进行相等性比较时,需要重写equals()方法。默认情况下,Java中的equals()方法是比较对象的引用是否相等,而不是对象的内容。通过重写equals()方法,你可以定义自己的相等性比较逻辑。

在前面的示例中,我们可以为Dog类重写equals()方法,以便在比较两个Dog对象时根据名称属性进行比较。以下是重写equals()方法的示例:

class Dog {
    private String name;
    public Dog(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Dog dog = (Dog) o;
        return name != null ? name.equals(dog.name) : dog.name == null;
    }
    @Override
    public int hashCode() {
        return name != null ? name.hashCode() : 0;
    }
    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                '}';
    }
}
public class ArrayListDogExample {
    public static void main(String[] args) {
        // 创建一个存放Dog对象的ArrayList
        List<Dog> dogs = new ArrayList<>();
        // 添加四条狗
        dogs.add(new Dog("Buddy"));
        dogs.add(new Dog("Max"));
        dogs.add(new Dog("Charlie"));
        dogs.add(new Dog("Lucy"));
        // 比较狗对象
        Dog targetDog = new Dog("Max");
        boolean containsMax = dogs.contains(targetDog);
        System.out.println("是否包含Max:" + containsMax);
    }
}

如上例,我们重写了equals()方法,使得两个Dog对象在名称相等时被视为相等。这使得我们可以使用contains()方法来检查dogs列表是否包含一个特定的Dog对象,而不仅仅是比较引用。

LinkedList独有方法

LinkedList是Java集合框架中的一种实现,它除了继承自List接口和Queue接口的方法外,还有一些独有的方法。以下是一些LinkedList独有的方法:

  1. addFirst(E e) / offerFirst(E e):将指定元素添加到链表的开头。
  2. addLast(E e) / offerLast(E e):将指定元素添加到链表的末尾。
  3. getFirst():返回链表的第一个元素,但不会删除它。如果链表为空,会抛出NoSuchElementException异常。
  4. getLast():返回链表的最后一个元素,但不会删除它。如果链表为空,会抛出NoSuchElementException异常。
  5. removeFirst() / pollFirst():移除并返回链表的第一个元素。如果链表为空,pollFirst()会返回null。
  6. removeLast() / pollLast():移除并返回链表的最后一个元素。如果链表为空,pollLast()会返回null。
  7. get(int index):返回链表中指定位置的元素。
  8. remove(int index):移除并返回链表中指定位置的元素。
  9. add(int index, E element):在指定位置插入指定元素。
  10. indexOf(Object o):返回指定元素在链表中首次出现的索引,如果不存在则返回-1。
  11. lastIndexOf(Object o):返回指定元素在链表中最后出现的索引,如果不存在则返回-1。
  12. listIterator(int index):返回一个ListIterator,从链表中的指定位置开始迭代。
  13. offer(E e):将指定元素添加到链表末尾,等效于addLast(E e)
  14. peek():返回链表的第一个元素,但不会删除它。如果链表为空,返回null。
  15. pop():从链表的开头弹出一个元素,等效于removeFirst()
  16. push(E e):将元素推入链表的开头,等效于addFirst(E e)

这些方法使得LinkedList在某些场景下非常有用,特别是需要频繁在链表的开头或末尾进行插入、删除和访问操作时。要注意的是,LinkedList的底层实现是一个双向链表,因此这些操作在链表中的效率通常比较高。

Set入门

Set是Java集合框架中的一种接口,它表示一个不包含重复元素的集合。Set不保证元素的顺序,即集合中的元素不按特定的顺序排列。在Set中,每个元素都是唯一的。Java中常见的Set实现类包括HashSet、LinkedHashSet和TreeSet。

以下是使用Set的入门示例:

import java.util.HashSet;
import java.util.Set;
public class SetExample {
    public static void main(String[] args) {
        // 创建一个HashSet来存储整数
        Set<Integer> numbers = new HashSet<>();
        // 添加元素到Set
        numbers.add(10);
        numbers.add(20);
        numbers.add(30);
        numbers.add(40);
        numbers.add(50);
        // 尝试添加重复元素
        boolean added = numbers.add(30);
        System.out.println("添加重复元素是否成功:" + added);
        // 打印Set中的元素
        System.out.println("Set中的元素:" + numbers);
        // 遍历Set中的元素
        System.out.println("遍历Set中的元素:");
        for (Integer num : numbers) {
            System.out.println(num);
        }
        // 移除元素
        numbers.remove(30);
        // 检查元素是否存在
        boolean contains = numbers.contains(20);
        System.out.println("是否包含元素 20:" + contains);
        // 清空Set
        numbers.clear();
        // 检查Set是否为空
        boolean isEmpty = numbers.isEmpty();
        System.out.println("Set是否为空:" + isEmpty);
    }
}

例中,我们使用了HashSet作为Set的实现类,但你也可以尝试使用其他的Set实现类。我们首先创建一个HashSet来存储整数,并演示了添加、重复添加、遍历、移除、检查元素是否存在以及清空Set的操作。

注意,Set不会包含重复的元素,因此尝试将重复元素添加到Set中时,add()方法会返回false。另外,Set的元素顺序是不确定的,因此遍历Set时元素的顺序可能与添加时不同。

Set去重现象

Set的一个主要特性是它不允许包含重复的元素,这就导致了Set自动去重的现象。

当你将重复的元素添加到Set中时,只会保留一个副本,其他重复的副本会被自动移除,从而确保Set中的元素始终是唯一的。

以下是一个示例来展示Set的去重现象:

import java.util.HashSet;
import java.util.Set;
public class SetDeduplicationExample {
    public static void main(String[] args) {
        Set<String> uniqueNames = new HashSet<>();
        // 添加元素到Set
        uniqueNames.add("Alice");
        uniqueNames.add("Bob");
        uniqueNames.add("Charlie");
        uniqueNames.add("Alice"); // 添加重复元素
        // 打印Set中的元素
        System.out.println("Set中的元素:" + uniqueNames);
        // 添加重复元素后,Set中只保留一个副本
        System.out.println("Set的大小:" + uniqueNames.size());
    }
}

在上述示例中,我们使用了HashSet来存储字符串,并添加了几个元素,包括一个重复元素"Alice"。当我们打印Set中的元素时,你会注意到重复的元素只保留了一个副本,而且Set的大小也是3而不是4,这是因为Set自动去重。

Set的去重特性使其在需要存储唯一元素的场景中非常有用,例如去重字符串列表、存储唯一的标识符等。

TreeSet算法依赖于一个比较接口

TreeSet在对元素进行排序和比较时依赖于一个比较接口,通常是Comparable接口或者通过传入一个Comparator对象来实现。

  1. 使用Comparable接口(自然排序):如果你希望TreeSet按照元素的自然顺序进行排序,那么被存储在TreeSet中的元素类必须实现Comparable接口,并重写compareTo()方法。这个方法决定了元素在集合中的顺序。
import java.util.TreeSet;
public class TreeSetExample {
    public static void main(String[] args) {
        TreeSet<Integer> numbers = new TreeSet<>();
        numbers.add(5);
        numbers.add(2);
        numbers.add(8);
        numbers.add(1);
        System.out.println("TreeSet中的元素:" + numbers);
    }
}

在这个示例中,整数类型实现了Comparable接口,默认按照自然顺序进行排序。

  1. 使用Comparator接口(定制排序):如果你希望TreeSet按照自定义的排序规则进行排序,你可以提供一个实现了Comparator接口的对象,然后将这个对象传递给TreeSet的构造方法。
import java.util.TreeSet;
import java.util.Comparator;
class Person {
    private String name;
    public Person(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}
public class TreeSetComparatorExample {
    public static void main(String[] args) {
        TreeSet<Person> people = new TreeSet<>(new Comparator<Person>() {
            @Override
            public int compare(Person p1, Person p2) {
                return p1.getName().compareTo(p2.getName());
            }
        });
        people.add(new Person("Alice"));
        people.add(new Person("Charlie"));
        people.add(new Person("Bob"));
        System.out.println("TreeSet中的人员:" + people);
    }
}

在这个示例中,我们创建了一个存储Person对象的TreeSet,通过匿名内部类实现了Comparator接口,以按照人名的字母顺序进行排序。

TreeSet可以根据自然顺序(通过Comparable接口)或者自定义排序规则(通过Comparator接口)来对元素进行排序,因此它在需要有序集合的场景中非常有用。

HashMap案例

演示了如何创建一个HashMap,添加键值对,访问和修改值,以及遍历键值对的基本操作:

import java.util.HashMap;
import java.util.Map;
public class HashMapExample {
    public static void main(String[] args) {
        // 创建一个HashMap来存储学生的成绩
        Map<String, Integer> scores = new HashMap<>();
        // 添加键值对
        scores.put("Alice", 95);
        scores.put("Bob", 80);
        scores.put("Charlie", 75);
        scores.put("David", 88);
        scores.put("Eve", 92);
        // 访问和修改值
        int charlieScore = scores.get("Charlie");
        System.out.println("Charlie的成绩:" + charlieScore);
        scores.put("Charlie", 85);
        System.out.println("修改后的Charlie的成绩:" + scores.get("Charlie"));
        // 遍历键值对
        System.out.println("所有学生的成绩:");
        for (Map.Entry<String, Integer> entry : scores.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
        // 检查键是否存在
        boolean containsBob = scores.containsKey("Bob");
        System.out.println("是否包含Bob:" + containsBob);
        // 删除键值对
        scores.remove("David");
        System.out.println("删除David后的学生成绩:" + scores);
    }
}

在这个案例中,我们创建了一个HashMap来存储学生的成绩,其中键是学生的姓名(String),值是学生的分数(Integer)。

我们展示了如何添加键值对、访问和修改值、遍历键值对、检查键是否存在以及删除键值对等操作。

HashMap是一种常用的键值对存储数据结构,它提供了高效的查找和插入操作。

注意,HashMap的键是唯一的,因此如果添加具有相同键的键值对,新值会覆盖旧值。

map常用方法

下面是一些常用的Map接口的方法,包括常见的操作和功能:

  1. put(K key, V value):将键值对添加到Map中,如果键已经存在,则会替换对应的值。
  2. get(Object key):根据键获取对应的值,如果键不存在,则返回null。
  3. remove(Object key):根据键移除对应的键值对。
  4. containsKey(Object key):检查Map中是否包含指定的键。
  5. containsValue(Object value):检查Map中是否包含指定的值。
  6. size():返回Map中键值对的数量。
  7. isEmpty():检查Map是否为空。
  8. clear():清空Map,移除所有键值对。
  9. keySet():返回包含所有键的Set集合。
  10. values():返回包含所有值的Collection集合。
  11. entrySet():返回包含所有键值对的Set集合(Map.Entry类型)。
  12. putAll(Map<? extends K, ? extends V> m):将另一个Map中的所有键值对添加到当前Map中。
  13. getOrDefault(Object key, V defaultValue):根据键获取对应的值,如果键不存在,则返回默认值。
  14. compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction):根据键对值进行计算,用于更新或创建键值对。
  15. forEach(BiConsumer<? super K, ? super V> action):对Map中的每个键值对执行指定的操作。
  16. replace(K key, V value):替换指定键的值,如果键不存在则不做任何操作。
  17. replace(K key, V oldValue, V newValue):替换指定键的旧值为新值,只有在键对应的值为旧值时才替换。
  18. replaceAll(BiFunction<? super K, ? super V, ? extends V> function):对Map中的每个键值对应用指定的操作。

泛型入门

泛型(Generics)是Java中一种强大的特性,它允许你在类、接口、方法的定义中使用类型参数,从而可以在编译时指定具体的数据类型,提高代码的重用性和类型安全性。泛型在集合框架和通用算法中广泛使用。

以下是关于泛型的一些入门示例和概念:

1. 泛型类(Generic Class):

public class Box<T> {
    private T value;
    public Box(T value) {
        this.value = value;
    }
    public T getValue() {
        return value;
    }
}
public class GenericsExample {
    public static void main(String[] args) {
        Box<Integer> intBox = new Box<>(42);
        int intValue = intBox.getValue();
        System.out.println("Value: " + intValue);
        Box<String> stringBox = new Box<>("Hello, Generics!");
        String stringValue = stringBox.getValue();
        System.out.println("Value: " + stringValue);
    }
}

例中,我们定义了一个泛型类Box<T>,它可以存储任意类型的值。在创建Box对象时,通过指定类型参数来指定具体的数据类型。这使得我们可以在不同的上下文中重用Box类,同时在编译时进行类型检查。

2. 泛型方法(Generic Method):

public class GenericsExample {
    public static <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.print(element + " ");
        }
        System.out.println();
    }
    public static void main(String[] args) {
        Integer[] intArray = {1, 2, 3, 4, 5};
        String[] stringArray = {"one", "two", "three", "four", "five"};
        printArray(intArray);
        printArray(stringArray);
    }
}

例中,我们定义了一个泛型方法printArray,它可以接受不同类型的数组作为参数,并打印数组中的元素。

通过使用<T>来声明泛型类型参数。

泛型的优势在于它提供了类型安全性和代码重用性,可以将数据类型相关的问题在编译时而不是运行时捕获,从而减少错误和调试难度。通过泛型,你可以编写更通用和灵活的代码。

使用泛型

使用泛型可以让你在类、接口、方法中定义一种通用的数据类型,这样可以在编写代码时更具灵活性和重用性。以下是使用泛型的一些示例和概念:

1. 泛型类(Generic Class):

public class Box<T> {
    private T value;
    public Box(T value) {
        this.value = value;
    }
    public T getValue() {
        return value;
    }
}
public class GenericsExample {
    public static void main(String[] args) {
        Box<Integer> intBox = new Box<>(42);
        int intValue = intBox.getValue();
        System.out.println("Value: " + intValue);
        Box<String> stringBox = new Box<>("Hello, Generics!");
        String stringValue = stringBox.getValue();
        System.out.println("Value: " + stringValue);
    }
}

例中,我们定义了一个泛型类Box<T>,可以用来存储不同类型的值。我们在创建Box对象时,通过指定类型参数来指定具体的数据类型。

2. 泛型方法(Generic Method):

public class GenericsExample {
    public static <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.print(element + " ");
        }
        System.out.println();
    }
    public static void main(String[] args) {
        Integer[] intArray = {1, 2, 3, 4, 5};
        String[] stringArray = {"one", "two", "three", "four", "five"};
        printArray(intArray);
        printArray(stringArray);
    }
}

定义一个泛型方法printArray,它可以接受不同类型的数组作为参数,并打印数组中的元素。通过使用<T>来声明泛型类型参数。

使用泛型可以提高代码的重用性,减少类型相关的错误,并增加代码的可读性。它在集合框架、算法和许多其他场景中广泛使用,帮助你更灵活地处理不同类型的数据。

迭代器Iterator

迭代器(Iterator)是一种用于遍历集合(如List、Set、Map等)中元素的对象。

它提供了一种统一的方式来访问集合中的元素,而无需关心集合的底层实现。

以下是使用迭代器进行集合遍历的示例:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class IteratorExample {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Alice");
        names.add("Bob");
        names.add("Charlie");
        names.add("David");
        // 获取集合的迭代器
        Iterator<String> iterator = names.iterator();
        // 使用迭代器遍历集合
        while (iterator.hasNext()) {
            String name = iterator.next();
            System.out.println(name);
        }
    }
}

我们首先创建了一个ArrayList来存储字符串,然后通过iterator()方法获取了集合的迭代器。接着,使用while循环和hasNext()方法检查是否还有下一个元素,如果有就使用next()方法获取并打印元素。这样,我们可以逐个遍历集合中的元素。

迭代器提供了一种遍历集合的通用方式,不依赖于集合的具体实现,因此你可以在不同类型的集合上使用相同的遍历逻辑。它也支持在遍历过程中对集合进行增删操作,但要注意避免在迭代过程中修改集合结构,以免引发并发修改异常(ConcurrentModificationException)。

Collections集合框架工具类

Java提供了java.util.Collections类作为集合框架的工具类,它包含了一系列静态方法,用于操作和处理集合对象。Collections类提供了许多便捷的方法来对集合进行排序、查找、替换、同步等操作。以下是一些常用的Collections类方法示例:

1. 排序集合:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CollectionsSortExample {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(5);
        numbers.add(2);
        numbers.add(8);
        numbers.add(1);
        System.out.println("排序前:" + numbers);
        Collections.sort(numbers);
        System.out.println("排序后:" + numbers);
    }
}

2. 查找最大/最小值:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CollectionsMinMaxExample {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(5);
        numbers.add(2);
        numbers.add(8);
        numbers.add(1);
        int min = Collections.min(numbers);
        int max = Collections.max(numbers);
        System.out.println("最小值:" + min);
        System.out.println("最大值:" + max);
    }
}

3. 集合反转:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CollectionsReverseExample {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Alice");
        names.add("Bob");
        names.add("Charlie");
        System.out.println("反转前:" + names);
        Collections.reverse(names);
        System.out.println("反转后:" + names);
    }
}

4. 随机洗牌:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CollectionsShuffleExample {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(1);
        numbers.add(2);
        numbers.add(3);
        numbers.add(4);
        numbers.add(5);
        System.out.println("洗牌前:" + numbers);
        Collections.shuffle(numbers);
        System.out.println("洗牌后:" + numbers);
    }
}

这些示例演示了Collections类的一些常见用法,它可以帮助我们更方便地操作和处理集合对象。注意,Collections类中的方法大多数都是静态方法,因此不需要实例化该类即可调用这些方法。

相关文章
|
28天前
|
算法 Java 数据处理
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。HashSet基于哈希表实现,提供高效的元素操作;TreeSet则通过红黑树实现元素的自然排序,适合需要有序访问的场景。本文通过示例代码详细介绍了两者的特性和应用场景。
37 6
|
28天前
|
存储 Java
深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。
【10月更文挑战第16天】本文深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。HashSet基于哈希表实现,添加元素时根据哈希值分布,遍历时顺序不可预测;而TreeSet利用红黑树结构,按自然顺序或自定义顺序存储元素,确保遍历时有序输出。文章还提供了示例代码,帮助读者更好地理解这两种集合类型的使用场景和内部机制。
38 3
|
28天前
|
存储 Java 数据处理
Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位
【10月更文挑战第16天】Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位。本文通过快速去重和高效查找两个案例,展示了Set如何简化数据处理流程,提升代码效率。使用HashSet可轻松实现数据去重,而contains方法则提供了快速查找的功能,彰显了Set在处理大量数据时的优势。
32 2
|
27天前
|
XML Java 编译器
Java学习十六—掌握注解:让编程更简单
Java 注解(Annotation)是一种特殊的语法结构,可以在代码中嵌入元数据。它们不直接影响代码的运行,但可以通过工具和框架提供额外的信息,帮助在编译、部署或运行时进行处理。
86 43
Java学习十六—掌握注解:让编程更简单
|
8天前
|
Java
Java 8 引入的 Streams 功能强大,提供了一种简洁高效的处理数据集合的方式
Java 8 引入的 Streams 功能强大,提供了一种简洁高效的处理数据集合的方式。本文介绍了 Streams 的基本概念和使用方法,包括创建 Streams、中间操作和终端操作,并通过多个案例详细解析了过滤、映射、归并、排序、分组和并行处理等操作,帮助读者更好地理解和掌握这一重要特性。
17 2
|
8天前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
12天前
|
存储 Java
判断一个元素是否在 Java 中的 Set 集合中
【10月更文挑战第30天】使用`contains()`方法可以方便快捷地判断一个元素是否在Java中的`Set`集合中,但对于自定义对象,需要注意重写`equals()`方法以确保正确的判断结果,同时根据具体的性能需求选择合适的`Set`实现类。
|
12天前
|
Java 大数据 API
14天Java基础学习——第1天:Java入门和环境搭建
本文介绍了Java的基础知识,包括Java的简介、历史和应用领域。详细讲解了如何安装JDK并配置环境变量,以及如何使用IntelliJ IDEA创建和运行Java项目。通过示例代码“HelloWorld.java”,展示了从编写到运行的全过程。适合初学者快速入门Java编程。
|
12天前
|
存储 Java 开发者
在 Java 中,如何遍历一个 Set 集合?
【10月更文挑战第30天】开发者可以根据具体的需求和代码风格选择合适的遍历方式。增强for循环简洁直观,适用于大多数简单的遍历场景;迭代器则更加灵活,可在遍历过程中进行更多复杂的操作;而Lambda表达式和`forEach`方法则提供了一种更简洁的函数式编程风格的遍历方式。
|
12天前
|
Java 开发者