set集合(HashSet、LinkedHashSet、TreeSet)

目录

set集合:Java 中的 Set 是一种集合接口,属于 Java 集合框架(java.util 包)的一部分,它继承自 Collection 接口。Set 集合的核心特点是不允许包含重复元素,并且最多只能包含一个 null 元素(具体取决于HashSet、LinkedHashSet、TreeSet三个实现类)。

一、HashSet:

1、底层原理:

2、主要特性:

(1)无重复元素:添加重复元素时会被自动过滤(add() 方法返回 false),这是通过元素的 hashCode() 和 equals() 方法判断的。

(2)无序性:元素的存储顺序与添加顺序无关,且遍历顺序可能随时间变化(不保证稳定性).

(3)允许 null 元素:但最多只能存储一个 null(因为不允许重复)。

(4)高效性能:添加、删除、查询元素的平均时间复杂度为 O(1),这得益于哈希表的快速访问特性。

(5)非线程安全:多线程环境下需手动同步(如使用 Collections.synchronizedSet() 包装)。

3、初始化:

(1)public HashSet():创建一个默认为空的HashSet

(2)public HashSet(int initialCapacity, float loadFactor):initialCapacity(初始容量)指哈希表在创建时的初始桶(bucket)数量,默认值为 16。容量必须是 2 的幂次方(哈希表会自动调整为最接近的 2 的幂),这是为了优化哈希计算的效率。loadFactor(加载因子)是哈希表扩容的阈值比例,默认值为 0.75f,计算公式:扩容值 = 当前容量 × 加载因子。当元素数量超过阈值时,哈希表会自动扩容(容量翻倍),以减少哈希冲突。

(3)public HashSet(Collection c):创建一个容器内容为c的集合。

4、常用方法:

(1)public boolean add(E e):向集合中添加元素。

(2)public boolean remove(Object o):集合中删除元素。

(3)public void clear():清空集合元素。

(4)public int size():返回集合中元素的数量。

(5)boolean addAll(Collection c): 插入一个collection 集合,插入的时候只要成功的添加一个元素,返回值就为true。

(6)boolean isEmpty():判空

(7)boolean contains(Object o): 判断元素是否存在。

5、集合遍历:

二、LinkedHashSet:

1、底层原理:

2、主要特性:

(1)有序性:与 HashSet 的无序性不同,LinkedHashSet 会通过双向链表记录元素的插入顺序,因此遍历元素时会按照添加顺序输出。有序指的是元素放入的顺序

(2)无重复元素:和所有 Set 实现类一样,LinkedHashSet 不允许存储重复元素,判断重复的逻辑与 HashSet 一致(依赖 hashCode() 和 equals() 方法)。

(3)允许 null 元素:最多只能包含一个 null 元素。

(4)与HashSet 的初始化、常用方法、遍历一样

三、TreeSet:

1、底层实现:

2、主要特性:

(1)有序性:元素会按照自然排序(默认)或自定义排序规则进行排序,而非插入顺序。

(2)无重复元素:与所有 Set 实现类一样,不允许重复元素,重复元素会被自动过滤。

(3)不允许 null 元素:插入 null 会抛出 NullPointerException(因为需要比较元素大小)。

(4)性能:添加、删除、查询元素的时间复杂度为 O(log n),适合需要排序且频繁操作的场景。

(5)非线程安全:多线程环境下需手动同步(如使用 Collections.synchronizedSortedSet() 包装)。

3、初始化:

(1)public  TreeSet(): 构造一个空的有序set集合

(2)public TreeSet(Comparator comparator)构造一个传入排序规则的有序Set集合

(3)public TreeSet(Collection c):构造一个集合元素为c的有序Set集合

4、Set集合如何过滤重复元素

四、HashSet、LinkedHashSet、TreeSet三者区别:


set集合:Java 中的 Set 是一种集合接口,属于 Java 集合框架(java.util 包)的一部分,它继承自 Collection 接口。Set 集合的核心特点是不允许包含重复元素,并且最多只能包含一个 null 元素(具体取决于HashSet、LinkedHashSet、TreeSet三个实现类)。

一、HashSet:

1、底层原理:

    HashSet 内部通过 HashMap 实现(维护一个 HashMap 实例):存储的元素被作为 HashMap 的 key,而 value 则是一个固定的空对象(PRESENT)。元素的去重逻辑依赖于 HashMap 的 key 不重复特性.

2、主要特性:
(1)无重复元素:添加重复元素时会被自动过滤(add() 方法返回 false),这是通过元素的 hashCode() 和 equals() 方法判断的。
(2)无序性:元素的存储顺序与添加顺序无关,且遍历顺序可能随时间变化(不保证稳定性).
(3)允许 null 元素:但最多只能存储一个 null(因为不允许重复)。
(4)高效性能:添加、删除、查询元素的平均时间复杂度为 O(1),这得益于哈希表的快速访问特性。
(5)非线程安全:多线程环境下需手动同步(如使用 Collections.synchronizedSet() 包装)。
3、初始化:
(1)public HashSet():创建一个默认为空的HashSet
(2)public HashSet(int initialCapacity, float loadFactor):initialCapacity(初始容量)指哈希表在创建时的初始桶(bucket)数量,默认值为 16。容量必须是 2 的幂次方(哈希表会自动调整为最接近的 2 的幂),这是为了优化哈希计算的效率。loadFactor(加载因子)是哈希表扩容的阈值比例,默认值为 0.75f,计算公式:扩容值 = 当前容量 × 加载因子。当元素数量超过阈值时,哈希表会自动扩容(容量翻倍),以减少哈希冲突。
(3)public HashSet(Collection<? extends E> c):创建一个容器内容为c的集合。
4、常用方法:
(1)public boolean add(E e):向集合中添加元素。
(2)public boolean remove(Object o):集合中删除元素。
(3)public void clear():清空集合元素。
(4)public int size():返回集合中元素的数量。
(5)boolean addAll(Collection<? extends E> c): 插入一个collection 集合,插入的时候只要成功的添加一个元素,返回值就为true。
(6)boolean isEmpty():判空
(7)boolean contains(Object o): 判断元素是否存在。
       HashSet<String> hs1 = new HashSet<>();
        //添加元素集合
        boolean b1 = hs1.add("曹操");
        hs1.add("周瑜");
        hs1.add("晁盖");
        boolean b2 = hs1.add("曹操");
        System.out.println("第一次添加元素:"+b1);
        System.out.println("第二次添加元素:"+b2);

        //快速生成list集合
        List<String> list = Arrays.asList("小乔","大乔","诸葛亮","晁盖");
        boolean b3 = hs1.addAll(list);  //添加元素集合,只要成功的添加一个元素,返回值就为true
        System.out.println("添加集合是否成功:"+b3);
        System.out.println(hs1);
        System.out.println("元素个数为:"+hs1.size());
        System.out.println("元素是否存在:"+hs1.contains("小乔"));
        System.out.println("元素是否为空:"+hs1.isEmpty());

        //删除
        boolean b1=hs1.remove("小乔");
        System.out.println("删除元素是否成功:"+b1);
        System.out.println(hs1);

        //清空
        hs1.clear();
        System.out.println(hs1);
5、集合遍历:

迭代器遍历:Iterator<E> iterator()

        Iterator<String> itor = hs1.iterator();
        while(itor.hasNext()){
            System.out.println(itor.next());
        }
        System.out.println("===============");
        for (String str:
                hs1) {
            System.out.println(str);
        }

二、LinkedHashSet:

1、底层原理:

       基于 HashMap 实现(与 HashSet 相同),但额外通过链表记录元素顺序,本质是一个 "Linked HashMap" 的 key 集合。  

2、主要特性:
(1)有序性:与 HashSet 的无序性不同,LinkedHashSet 会通过双向链表记录元素的插入顺序,因此遍历元素时会按照添加顺序输出。有序指的是元素放入的顺序
(2)无重复元素:和所有 Set 实现类一样,LinkedHashSet 不允许存储重复元素,判断重复的逻辑与 HashSet 一致(依赖 hashCode() 和 equals() 方法)。
(3)允许 null 元素:最多只能包含一个 null 元素。
(4)与HashSet 的初始化、常用方法、遍历一样

三、TreeSet:

1、底层实现:

   TreeSet 内部依赖 TreeMap 实现(维护一个 TreeMap 实例):存储的元素被作为 TreeMap 的 key,value 是一个固定的空对象(PRESENT)。排序逻辑完全依赖 TreeMap 的 key 排序特性。元素必须可比较(要么实现 Comparable,要么提供 Comparator),否则会抛出 ClassCastException。排序规则需与 equals() 方法保持一致(即 compareTo() 返回 0 时,equals() 应返回 true),否则可能出现逻辑矛盾(如元素被视为不同但排序位置相同)。

2、主要特性:
(1)有序性:元素会按照自然排序(默认)或自定义排序规则进行排序,而非插入顺序。
(2)无重复元素:与所有 Set 实现类一样,不允许重复元素,重复元素会被自动过滤。
(3)不允许 null 元素:插入 null 会抛出 NullPointerException(因为需要比较元素大小)。
(4)性能:添加、删除、查询元素的时间复杂度为 O(log n),适合需要排序且频繁操作的场景。
(5)非线程安全:多线程环境下需手动同步(如使用 Collections.synchronizedSortedSet() 包装)。
3、初始化:
(1)public  TreeSet(): 构造一个空的有序set集合
(2)public TreeSet(Comparator<? super E> comparator)构造一个传入排序规则的有序Set集合
(3)public TreeSet(Collection<? extends E> c):构造一个集合元素为c的有序Set集合
4、Set集合如何过滤重复元素

核心原理:(1)hashCode() 方法:计算元素的哈希值,用于快速定位元素在底层数据结构(如哈希表)中的存储位置。两个相同的元素必须返回相同的哈希值。(2)equals() 方法:用于精确比较两个元素是否为 "逻辑上的相同元素"。当两个元素的哈希值相同时,会通过 equals() 进一步确认是否为重复元素。

自定义对象去重的关键:对于自定义类的对象,需同时重写 hashCode() 和 equals() 方法TreeSet 需保证排序逻辑与 equals() 一致),否则可能导致去重失效。

例如:

package com.yuan.treeset;

import java.util.Comparator;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;

public class Book implements Comparable<Book> {
    private String bookName;
    private String authorName;
    private int pageSize;
    private double price;

    public Book(String bookName, String authorName, int pageSize, double price) {
        this.bookName = bookName;
        this.authorName = authorName;
        this.pageSize = pageSize;
        this.price = price;

    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Book book = (Book) o;
        return pageSize == book.pageSize && Double.compare(book.price, price) == 0 && Objects.equals(bookName, book.bookName) && Objects.equals(authorName, book.authorName);
    }

    @Override
    public String toString() {
        return "Book{" +
                "bookName='" + bookName + '\'' +
                ", authorName='" + authorName + '\'' +
                ", pageSize=" + pageSize +
                ", price=" + price +
                '}' + "\n";
    }

    @Override
    public int compareTo(Book o) {
        int result = this.pageSize - o.pageSize;
        if (result == 0) {
            return this.bookName.compareTo(o.bookName);

        }
        return result;
    }


    @Override
    public int hashCode() {
        return Objects.hash(bookName, authorName, pageSize, price);
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public String getAuthorName() {
        return authorName;
    }

    public void setAuthorName(String authorName) {
        this.authorName = authorName;
    }

    public int getPageSize() {
        return pageSize;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }
}

class Demo01 {
    public static void main(String[] args) {
        //TreeSet是可排序的,且唯一的:
        // 在使用TreeSet时需要注意,一定要提供排序规则给对象(泛型类实现comparable接口或者对象提供Comparator对象)

        //1.无参的构造方法,排序规则使用泛型类型这个类定义的排序规则
        //2.有参构造TreeSet(Comparator comparator)排序比较规则按照传入的参数的规则为准
        Set<Book> set = new TreeSet<>();
        Book b1 = new Book("明朝那些事1", "当年明月1", 1231, 89.0);
        Book b2 = new Book("明朝那些事2", "当年明月2", 121, 89.0);
        Book b3 = new Book("明朝那些事3", "当年明月3", 11, 90.0);
        Book b4 = new Book("明朝那些事4", "当年明月1", 1231, 89.0);
        set.add(b1);
        set.add(b2);
        set.add(b3);
        set.add(b4);

        System.out.println(set);
    }

}

class Demo02 {
    public static void main(String[] args) {
        //如果泛型类型不是Comparable类型,必须要提供一个Comparator对象提供比较规则。
        Set<Book> set = new TreeSet<>(new Comparator<Book>() {
            @Override
            public int compare(Book o1, Book o2) {
                //按照书的价格进行比较
                if(o1.getPrice()>o2.getPrice()){
                    return 1;
                }else if (o1.getPrice() == o2.getPrice()) {
                    return 0;
                }else {
                    return -1;
                }
            }
        });
        Book b1 = new Book("明朝那些事1", "当年明月1", 1231, 89.0);
        Book b2 = new Book("明朝那些事2", "当年明月2", 121, 100.0);
        Book b3 = new Book("明朝那些事3", "当年明月3", 11, 90.0);
        Book b4 = new Book("明朝那些事4", "当年明月1", 1231, 89.0);
        set.add(b1);
        set.add(b2);
        set.add(b3);
        set.add(b4);

        System.out.println(set);
    }
}

四、HashSet、LinkedHashSet、TreeSet三者区别:

实现类排序特性底层结构时间复杂度(增删查)
HashSet无序哈希表O (1)(平均)
LinkedHashSet保持插入顺序哈希表 + 链表O (1)(平均)
TreeSet自然排序 / 自定义排序红黑树O(log n)

#include <stdio.h> #include <stdlib.h> #include <string.h> #include "list.h" /** * @brief 这个是内核链表的一个demo * 1. 第一自己需要的数据类型 ,其中必须包含一个 struct list_head 的变量 2. 定义头节点,并初始化 3. 增加结点,malloc自己的结构体,填入自己需要的数据 调用list_add ,把当前结点加入链表 4. 遍历所有元素list_for_each_entry_safe, */ typedef struct { int id; char name[50]; struct list_head node; } PER; int add_per(struct list_head *head, int id, char *name) { PER *per = malloc(sizeof(PER)); if (NULL == per) { perror("add_per malloc error\n"); return 1; } per->id = id; strcpy(per->name, name); //头插 // list_add(&per->node, head); list_add_tail(&per->node, head); return 0; } int show(struct list_head *head) { // 遍历所有数据, // pos 当前要访问的PER结构体指针 ,n是pos的下一个指针 , // head 链表的头结点 // member 在自定义的结构体中 结点的变量名 // list_for_each_entry_safe(pos, n, head, member) for PER *tmp; PER *next; list_for_each_entry_safe(tmp, next, head, node) { printf("%d %s\n", tmp->id, tmp->name); } return 0; } /** * @brief * * @param head * @param id 需要删除数据的编号 * @return int */ PER* find_per(struct list_head *head, char *name) { PER *tmp; list_for_each_entry(tmp, head, node) { if (strcmp(tmp->name, name) == 0) { printf("找到节点:id=%d, name=%s\n", tmp->id, tmp->name); return tmp; } } printf("未找到姓名为「%s」的节点\n", name); return NULL; } int modify_per(struct list_head *head, char *old_name, int new_id, char *new_name) { PER *target = find_per(head, old_name); if (target == NULL) { return 1; } target->id = new_id; if (strlen(new_name) >= sizeof(target->name)) { printf("无效名字"); return 2; } strcpy(target->name, new_name); printf("修改成功:原姓名「%s」→ 新id=%d, 新姓名「%s」\n", old_name, new_id, new_name); return 0; } int del_per(struct list_head *head, int id) { PER *tmp; PER *next; list_for_each_entry_safe(tmp, next, head, node) { if (tmp->id == id) { list_del(&tmp->node); free(tmp); } } return 0; } int main(int argc, char **argv) { //头结点,不包含有效数据,head->next 是链表中第一个有效数据 struct list_head head; //双向循环链表, 当前结点的prev,next 都指向自己 INIT_LIST_HEAD(&head); add_per(&head, 1, "zhagnsan"); add_per(&head, 2, "lisi"); add_per(&head, 3, "wangmazi"); add_per(&head, 4, "guanerge"); add_per(&head, 5, "liubei "); show(&head); del_per(&head, 1); printf("------------del--------------\n"); show(&head); find_per(&head, "lisi"); // system("pause"); return 0; } #ifndef _LINUX_LIST_H #define _LINUX_LIST_H #include <linux/stddef.h> #include <stdio.h> //#include <linux/poison.h> //#include <linux/prefetch.h> //#include <asm/system.h> /* * Simple doubly linked list implementation. * * Some of the internal functions ("__xxx") are useful when * manipulating whole lists rather than single entries, as * sometimes we already know the next/prev entries and we can * generate better code by using them directly rather than * using the generic single-entry routines. */ // #define LIST_POISON1 ((void *) 0x00100100) // #define LIST_POISON2 ((void *) 0x00200200) #define LIST_POISON1 ((void *) 0) #define LIST_POISON2 ((void *) 0) #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) struct list_head { struct list_head *next, *prev; }; #define LIST_HEAD_INIT(name) \ { \ &(name), &(name) \ } #define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT (name) static inline void INIT_LIST_HEAD (struct list_head *list) { list->next = list; list->prev = list; } /* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ #ifndef CONFIG_DEBUG_LIST static inline void __list_add (struct list_head *new, struct list_head *prev, struct list_head *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } #else extern void __list_add (struct list_head *new, struct list_head *prev, struct list_head *next); #endif /** * list_add - add a new entry * @new: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */ static inline void list_add (struct list_head *new, struct list_head *head) { __list_add (new, head, head->next); } /** * list_add_tail - add a new entry * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ static inline void list_add_tail (struct list_head *new, struct list_head *head) { __list_add (new, head->prev, head); } /* * Delete a list entry by making the prev/next entries * point to each other. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_del (struct list_head *prev, struct list_head *next) { next->prev = prev; prev->next = next; } /** * list_del - deletes entry from list. * @entry: the element to delete from the list. * Note: list_empty() on entry does not return true after this, the entry is * in an undefined state. */ #ifndef CONFIG_DEBUG_LIST static inline void list_del (struct list_head *entry) { __list_del (entry->prev, entry->next); entry->next = LIST_POISON1; entry->prev = LIST_POISON2; } #else extern void list_del (struct list_head *entry); #endif /** * list_replace - replace old entry by new one * @old : the element to be replaced * @new : the new element to insert * * If @old was empty, it will be overwritten. */ static inline void list_replace (struct list_head *old, struct list_head *new) { new->next = old->next; new->next->prev = new; new->prev = old->prev; new->prev->next = new; } static inline void list_replace_init (struct list_head *old, struct list_head *new) { list_replace (old, new); INIT_LIST_HEAD (old); } /** * list_del_init - deletes entry from list and reinitialize it. * @entry: the element to delete from the list. */ static inline void list_del_init (struct list_head *entry) { __list_del (entry->prev, entry->next); INIT_LIST_HEAD (entry); } /** * list_move - delete from one list and add as another's head * @list: the entry to move * @head: the head that will precede our entry */ static inline void list_move (struct list_head *list, struct list_head *head) { __list_del (list->prev, list->next); list_add (list, head); } /** * list_move_tail - delete from one list and add as another's tail * @list: the entry to move * @head: the head that will follow our entry */ static inline void list_move_tail (struct list_head *list, struct list_head *head) { __list_del (list->prev, list->next); list_add_tail (list, head); } /** * list_is_last - tests whether @list is the last entry in list @head * @list: the entry to test * @head: the head of the list */ static inline int list_is_last (const struct list_head *list, const struct list_head *head) { return list->next == head; } /** * list_empty - tests whether a list is empty * @head: the list to test. */ static inline int list_empty (const struct list_head *head) { return head->next == head; } /** * list_empty_careful - tests whether a list is empty and not being modified * @head: the list to test * * Description: * tests whether a list is empty _and_ checks that no other CPU might be * in the process of modifying either member (next or prev) * * NOTE: using list_empty_careful() without synchronization * can only be safe if the only activity that can happen * to the list entry is list_del_init(). Eg. it cannot be used * if another CPU could re-list_add() it. */ static inline int list_empty_careful (const struct list_head *head) { struct list_head *next = head->next; return (next == head) && (next == head->prev); } /** * list_is_singular - tests whether a list has just one entry. * @head: the list to test. */ static inline int list_is_singular (const struct list_head *head) { return !list_empty (head) && (head->next == head->prev); } static inline void __list_cut_position (struct list_head *list, struct list_head *head, struct list_head *entry) { struct list_head *new_first = entry->next; list->next = head->next; list->next->prev = list; list->prev = entry; entry->next = list; head->next = new_first; new_first->prev = head; } /** * list_cut_position - cut a list into two * @list: a new list to add all removed entries * @head: a list with entries * @entry: an entry within head, could be the head itself * and if so we won't cut the list * * This helper moves the initial part of @head, up to and * including @entry, from @head to @list. You should * pass on @entry an element you know is on @head. @list * should be an empty list or a list you do not care about * losing its data. * */ static inline void list_cut_position (struct list_head *list, struct list_head *head, struct list_head *entry) { if (list_empty (head)) return; if (list_is_singular (head) && (head->next != entry && head != entry)) return; if (entry == head) INIT_LIST_HEAD (list); else __list_cut_position (list, head, entry); } static inline void __list_splice (const struct list_head *list, struct list_head *prev, struct list_head *next) { struct list_head *first = list->next; struct list_head *last = list->prev; first->prev = prev; prev->next = first; last->next = next; next->prev = last; } /** * list_splice - join two lists, this is designed for stacks * @list: the new list to add. * @head: the place to add it in the first list. */ static inline void list_splice (const struct list_head *list, struct list_head *head) { if (!list_empty (list)) __list_splice (list, head, head->next); } /** * list_splice_tail - join two lists, each list being a queue * @list: the new list to add. * @head: the place to add it in the first list. */ static inline void list_splice_tail (struct list_head *list, struct list_head *head) { if (!list_empty (list)) __list_splice (list, head->prev, head); } /** * list_splice_init - join two lists and reinitialise the emptied list. * @list: the new list to add. * @head: the place to add it in the first list. * * The list at @list is reinitialised */ static inline void list_splice_init (struct list_head *list, struct list_head *head) { if (!list_empty (list)) { __list_splice (list, head, head->next); INIT_LIST_HEAD (list); } } /** * list_splice_tail_init - join two lists and reinitialise the emptied list * @list: the new list to add. * @head: the place to add it in the first list. * * Each of the lists is a queue. * The list at @list is reinitialised */ static inline void list_splice_tail_init (struct list_head *list, struct list_head *head) { if (!list_empty (list)) { __list_splice (list, head->prev, head); INIT_LIST_HEAD (list); } } /** * list_entry - get the struct for this entry * @ptr: the &struct list_head pointer. * @type: the type of the struct this is embedded in. * @member: the name of the list_struct within the struct. */ #define list_entry(ptr, type, member) container_of (ptr, type, member) /** * list_first_entry - get the first element from a list * @ptr: the list head to take the element from. * @type: the type of the struct this is embedded in. * @member: the name of the list_struct within the struct. * * Note, that list is expected to be not empty. */ #define list_first_entry(ptr, type, member) \ list_entry ((ptr)->next, type, member) /** * list_for_each - iterate over a list * @pos: the &struct list_head to use as a loop cursor. * @head: the head for your list. */ #define list_for_each(pos, head) \ for (pos = (head)->next; prefetch (pos->next), pos != (head); \ pos = pos->next) /** * __list_for_each - iterate over a list * @pos: the &struct list_head to use as a loop cursor. * @head: the head for your list. * * This variant differs from list_for_each() in that it's the * simplest possible list iteration code, no prefetching is done. * Use this for code that knows the list to be very short (empty * or 1 entry) most of the time. */ #define __list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) /** * list_for_each_prev - iterate over a list backwards * @pos: the &struct list_head to use as a loop cursor. * @head: the head for your list. */ #define list_for_each_prev(pos, head) \ for (pos = (head)->prev; prefetch (pos->prev), pos != (head); \ pos = pos->prev) /** * list_for_each_safe - iterate over a list safe against removal of list entry * @pos: the &struct list_head to use as a loop cursor. * @n: another &struct list_head to use as temporary storage * @head: the head for your list. */ #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) /** * list_for_each_prev_safe - iterate over a list backwards safe against removal * of list entry * @pos: the &struct list_head to use as a loop cursor. * @n: another &struct list_head to use as temporary storage * @head: the head for your list. */ #define list_for_each_prev_safe(pos, n, head) \ for (pos = (head)->prev, n = pos->prev; \ prefetch (pos->prev), pos != (head); pos = n, n = pos->prev) /** * list_for_each_entry - iterate over list of given type * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry(pos, head, member) \ for (pos = list_entry ((head)->next, typeof(*pos), member); \ prefetch (pos->member.next), &pos->member != (head); \ pos = list_entry (pos->member.next, typeof(*pos), member)) /** * list_for_each_entry_reverse - iterate backwards over list of given type. * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_reverse(pos, head, member) \ for (pos = list_entry ((head)->prev, typeof(*pos), member); \ prefetch (pos->member.prev), &pos->member != (head); \ pos = list_entry (pos->member.prev, typeof(*pos), member)) /** * list_prepare_entry - prepare a pos entry for use in * list_for_each_entry_continue() * @pos: the type * to use as a start point * @head: the head of the list * @member: the name of the list_struct within the struct. * * Prepares a pos entry for use as a start point in * list_for_each_entry_continue(). */ #define list_prepare_entry(pos, head, member) \ ((pos) ?: list_entry (head, typeof(*pos), member)) /** * list_for_each_entry_continue - continue iteration over list of given type * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_struct within the struct. * * Continue to iterate over list of given type, continuing after * the current position. */ #define list_for_each_entry_continue(pos, head, member) \ for (pos = list_entry (pos->member.next, typeof(*pos), member); \ prefetch (pos->member.next), &pos->member != (head); \ pos = list_entry (pos->member.next, typeof(*pos), member)) /** * list_for_each_entry_continue_reverse - iterate backwards from the given * point * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_struct within the struct. * * Start to iterate over list of given type backwards, continuing after * the current position. */ #define list_for_each_entry_continue_reverse(pos, head, member) \ for (pos = list_entry (pos->member.prev, typeof(*pos), member); \ prefetch (pos->member.prev), &pos->member != (head); \ pos = list_entry (pos->member.prev, typeof(*pos), member)) /** * list_for_each_entry_from - iterate over list of given type from the current * point * @pos: the type * to use as a loop cursor. * @head: the head for your list. * @member: the name of the list_struct within the struct. * * Iterate over list of given type, continuing from current position. */ #define list_for_each_entry_from(pos, head, member) \ for (; prefetch (pos->member.next), &pos->member != (head); \ pos = list_entry (pos->member.next, typeof(*pos), member)) /** * list_for_each_entry_safe - iterate over list of given type safe against * removal of list entry * @pos: the type * to use as a loop cursor. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_safe(pos, n, head, member) \ for (pos = list_entry ((head)->next, typeof(*pos), member), \ n = list_entry (pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry (n->member.next, typeof(*n), member)) /** * list_for_each_entry_safe_continue * @pos: the type * to use as a loop cursor. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. * * Iterate over list of given type, continuing after current point, * safe against removal of list entry. */ #define list_for_each_entry_safe_continue(pos, n, head, member) \ for (pos = list_entry (pos->member.next, typeof(*pos), member), \ n = list_entry (pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry (n->member.next, typeof(*n), member)) /** * list_for_each_entry_safe_from * @pos: the type * to use as a loop cursor. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. * * Iterate over list of given type from current point, safe against * removal of list entry. */ #define list_for_each_entry_safe_from(pos, n, head, member) \ for (n = list_entry (pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry (n->member.next, typeof(*n), member)) /** * list_for_each_entry_safe_reverse * @pos: the type * to use as a loop cursor. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. * * Iterate backwards over list of given type, safe against removal * of list entry. */ #define list_for_each_entry_safe_reverse(pos, n, head, member) \ for (pos = list_entry ((head)->prev, typeof(*pos), member), \ n = list_entry (pos->member.prev, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry (n->member.prev, typeof(*n), member)) /* * Double linked lists with a single pointer list head. * Mostly useful for hash tables where the two pointer list head is * too wasteful. * You lose the ability to access the tail in O(1). */ struct hlist_head { struct hlist_node *first; }; struct hlist_node { struct hlist_node *next, **pprev; }; #define HLIST_HEAD_INIT \ { \ .first = NULL \ } #define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } #define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) static inline void INIT_HLIST_NODE (struct hlist_node *h) { h->next = NULL; h->pprev = NULL; } static inline int hlist_unhashed (const struct hlist_node *h) { return !h->pprev; } static inline int hlist_empty (const struct hlist_head *h) { return !h->first; } static inline void __hlist_del (struct hlist_node *n) { struct hlist_node *next = n->next; struct hlist_node **pprev = n->pprev; *pprev = next; if (next) next->pprev = pprev; } static inline void hlist_del (struct hlist_node *n) { __hlist_del (n); n->next = LIST_POISON1; n->pprev = LIST_POISON2; } static inline void hlist_del_init (struct hlist_node *n) { if (!hlist_unhashed (n)) { __hlist_del (n); INIT_HLIST_NODE (n); } } static inline void hlist_add_head (struct hlist_node *n, struct hlist_head *h) { struct hlist_node *first = h->first; n->next = first; if (first) first->pprev = &n->next; h->first = n; n->pprev = &h->first; } /* next must be != NULL */ static inline void hlist_add_before (struct hlist_node *n, struct hlist_node *next) { n->pprev = next->pprev; n->next = next; next->pprev = &n->next; *(n->pprev) = n; } static inline void hlist_add_after (struct hlist_node *n, struct hlist_node *next) { next->next = n->next; n->next = next; next->pprev = &n->next; if (next->next) next->next->pprev = &next->next; } /* * Move a list from one list head to another. Fixup the pprev * reference of the first entry if it exists. */ static inline void hlist_move_list (struct hlist_head *old, struct hlist_head *new) { new->first = old->first; if (new->first) new->first->pprev = &new->first; old->first = NULL; } #define hlist_entry(ptr, type, member) container_of (ptr, type, member) #define hlist_for_each(pos, head) \ for (pos = (head)->first; pos && ({ \ prefetch (pos->next); \ 1; \ }); \ pos = pos->next) #define hlist_for_each_safe(pos, n, head) \ for (pos = (head)->first; pos && ({ \ n = pos->next; \ 1; \ }); \ pos = n) /** * hlist_for_each_entry - iterate over list of given type * @tpos: the type * to use as a loop cursor. * @pos: the &struct hlist_node to use as a loop cursor. * @head: the head for your list. * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry(tpos, pos, head, member) \ for (pos = (head)->first; \ pos && ({ \ prefetch (pos->next); \ 1; \ }) \ && ({ \ tpos = hlist_entry (pos, typeof(*tpos), member); \ 1; \ }); \ pos = pos->next) /** * hlist_for_each_entry_continue - iterate over a hlist continuing after * current point * @tpos: the type * to use as a loop cursor. * @pos: the &struct hlist_node to use as a loop cursor. * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry_continue(tpos, pos, member) \ for (pos = (pos)->next; \ pos && ({ \ prefetch (pos->next); \ 1; \ }) \ && ({ \ tpos = hlist_entry (pos, typeof(*tpos), member); \ 1; \ }); \ pos = pos->next) /** * hlist_for_each_entry_from - iterate over a hlist continuing from current * point * @tpos: the type * to use as a loop cursor. * @pos: the &struct hlist_node to use as a loop cursor. * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry_from(tpos, pos, member) \ for (; pos && ({ \ prefetch (pos->next); \ 1; \ }) \ && ({ \ tpos = hlist_entry (pos, typeof(*tpos), member); \ 1; \ }); \ pos = pos->next) /** * hlist_for_each_entry_safe - iterate over list of given type safe against * removal of list entry * @tpos: the type * to use as a loop cursor. * @pos: the &struct hlist_node to use as a loop cursor. * @n: another &struct hlist_node to use as temporary storage * @head: the head for your list. * @member: the name of the hlist_node within the struct. */ #define hlist_for_each_entry_safe(tpos, pos, n, head, member) \ for (pos = (head)->first; \ pos && ({ \ n = pos->next; \ 1; \ }) \ && ({ \ tpos = hlist_entry (pos, typeof(*tpos), member); \ 1; \ }); \ pos = n) #endif 错误信息为In file included from main.c:4:0: main.c: In function ‘find_per’: list.h:417:8: warning: implicit declaration of function ‘prefetch’; did you mean ‘rpmatch’? [-Wimplicit-function-declaration] prefetch (pos->member.next), &pos->member != (head); \ ^ main.c:63:5: note: in expansion of macro ‘list_for_each_entry’ list_for_each_entry(tmp, head, node) ^~~~~~~~~~~~~~~~~~~ /tmp/cce8BVSj.o: In function `find_per': main.c:(.text+0x297): undefined reference to `prefetch' collect2: error: ld returned 1 exit status,该如何修改
08-24
<think>我们正在解决一个关于Linux内核链表使用中的编译错误问题。错误提示是prefetch函数未定义。根据Linux内核编程经验,prefetch是内核中用于数据预取的一个宏或函数,通常在头文件中定义。 在用户代码中直接使用内核链表时,可能会遇到这样的问题,因为用户空间程序不能直接包含内核头文件。但是,如果是在内核模块中编写代码,那么应该包含正确的头文件。 首先,我们需要确定代码的运行环境:是内核模块还是用户空间程序? 根据问题描述,用户在使用Linux内核链表,这通常是在内核模块中。因此,我们需要在内核模块中包含正确的头文件。 在内核中,prefetch通常定义在<linux/prefetch.h>中。因此,我们可以尝试包含这个头文件。 但是,请注意,在较新的内核版本中,这个头文件可能已经被移动或改名。例如,在5.x版本的内核中,我们通常包含<linux/list.h>就足够了,因为list.h可能会包含prefetch.h或者已经不再需要显式包含prefetch.h。 然而,如果错误仍然存在,可能是因为prefetch宏在内核中是通过体系结构相关的头文件实现的,或者在某些配置下未定义。 另一种可能性:用户可能是在用户空间程序中使用内核链表(虽然不常见,但有时为了复用代码会这样做)。在这种情况下,我们需要提供一个用户空间版本的prefetch。通常,用户空间的prefetch可以定义为空,或者使用用户空间预取指令(如__builtin_prefetch),但需要根据具体环境调整。 由于问题没有明确是内核模块还是用户空间,我们分两种情况讨论: 情况1:内核模块 - 确保包含<linux/list.h>,因为list.h已经包含了链表操作所需的声明和定义。 - 如果包含list.h后仍然出现prefetch未定义,那么尝试包含<linux/prefetch.h>。 情况2:用户空间程序 - 我们需要定义自己的prefetch宏。在用户空间,我们可以将其定义为空,或者使用gcc的内建函数__builtin_prefetch。例如: #define prefetch(x) __builtin_prefetch(x) 或者如果不需要预取,可以定义为空: #define prefetch(x) do { } while (0) 但是,请注意,内核链表实现中可能使用了prefetch,所以如果是在用户空间使用内核链表代码,那么需要复制链表相关的代码并修改,将prefetch的调用替换为用户空间的版本。 根据问题描述,用户是在使用Linux内核链表,所以更可能是内核模块。因此,我们首先建议在内核模块中添加头文件包含。 具体步骤: 1. 在代码文件的开头,确保已经包含了<linux/list.h>。 2. 如果已经包含,但仍然报错,那么尝试包含<linux/prefetch.h>。 如果上述方法不能解决问题,可能是内核配置问题,但这种情况较少见。 因此,我们给出以下解决方案: 在内核模块中,添加以下包含语句: #include <linux/prefetch.h> 如果问题仍然存在,请检查内核版本,并查看该版本中prefetch宏的定义位置。 另外,注意prefetch函数在链表操作中通常用于预取下一个节点的数据以提高性能,所以它必须是可用的。 如果用户是在用户空间使用内核链表代码,那么需要修改链表头文件(例如,从内核源码中复制list.h到用户项目),并修改其中关于prefetch的部分,将其替换为用户空间的实现。 但是,由于用户没有明确说明环境,我们假设是内核模块(因为问题标签是Linux内核链表)。因此,我们推荐包含<linux/prefetch.h>。 如果用户已经包含了<linux/prefetch.h>,那么可能是头文件中有条件编译导致prefetch没有被定义。这时需要检查内核配置(例如,是否启用了CONFIG_ARCH_HAS_PREFETCH等),但这比较复杂。一个简单的解决方法是,如果包含prefetch.h后仍然未定义,可以尝试在包含之前定义CONFIG_ARCH_HAS_PREFETCH,但这不是标准做法。 更常见的做法是,在包含头文件之前确保定义了__KERNEL__宏(在内核模块中,通常通过Makefile定义,不需要在代码中显式定义)。 因此,我们建议用户检查模块的编译选项是否正确定义了__KERNEL__宏。 总结: 1. 在内核模块中,确保包含<linux/prefetch.h>。 2. 如果已经包含,则检查模块的编译选项是否包含-D__KERNEL__(实际上,在内核模块的Makefile中,通常是通过内核构建系统自动设置的,所以如果使用标准的内核模块构建方式,应该没有问题)。 如果用户是在用户空间,那么需要重新实现prefetch。 由于问题描述不够详细,我们给出两种情况的示例: 情况1:内核模块 在代码中添加: #include <linux/prefetch.h> 情况2:用户空间 在包含内核链表头文件之前,添加: #define prefetch(x) __builtin_prefetch(x) 或者,如果不想使用预取,则定义为空。 注意:用户空间使用内核链表需要复制内核的链表头文件(list.h)并进行修改,或者自己实现一个用户空间版本的链表。 根据经验,用户的问题更可能是情况1(内核模块)。因此,我们首先推荐情况1的解决方案。 如果用户的情况是用户空间,请按照情况2处理。 最后,我们生成相关问题: §§相关问题§§ 1. 如何在内核模块中正确包含Linux内核头文件? 2. 在用户空间程序中使用Linux内核链表需要注意什么? 3. 如何为不同的体系结构实现数据预取(prefetch)? 4. 编译内核模块时遇到未定义的函数或宏应该如何处理?</think>### 修复 Linux 内核链表中的 `prefetch` 未定义错误 当在使用 Linux 内核链表时遇到 `prefetch` 未定义错误,通常是因为代码直接复制了内核链表实现(如 `list.h`),但未正确处理内核特有的预取机制。以下是解决方法: #### 解决方案 1. **添加头文件包含** 在内核模块代码开头添加: ```c #include <linux/prefetch.h> ``` 此头文件提供了 `prefetch()` 宏的定义,用于数据预取优化。 2. **用户空间适配(非内核模块)** 若在**用户空间程序**中使用内核链表: ```c // 手动定义空预取宏(不推荐,可能影响性能) #define prefetch(x) do { } while (0) // 或使用 GCC 内置预取指令 #define prefetch(x) __builtin_prefetch(x) ``` 3. **检查链表实现来源** 确保使用的 `list.h` 来自与当前内核版本匹配的源码树(避免版本不兼容)。 #### 错误原因分析 - `prefetch` 是 Linux 内核内部的性能优化宏,依赖于体系结构(如 x86/ARM)。 - 直接复制内核链表代码到用户空间时,会因缺少内核头文件导致编译失败。 - 在内核模块中遗漏 `<linux/prefetch.h>` 也会触发此错误。 #### 完整示例(内核模块) ```c #include <linux/module.h> #include <linux/list.h> #include <linux/prefetch.h> // 关键修复 struct my_data { int value; struct list_head list; }; static int __init my_init(void) { struct list_head my_list; INIT_LIST_HEAD(&my_list); // 链表操作代码... return 0; } module_init(my_init); ``` ### 相关问题 1. **如何安全地在用户空间程序中使用 Linux 内核数据结构?** [需考虑内存管理、头文件隔离和 API 适配性] 2. **Linux 内核链表的性能优化机制有哪些?** [如 `prefetch` 原理、缓存对齐设计等] 3. **编译内核模块时遇到未定义符号应如何调试?** [涉及 `modpost` 阶段检查、`EXPORT_SYMBOL` 分析和版本匹配] 4. **`__builtin_prefetch` 在用户空间程序中的实际效果如何验证?** [可通过性能剖析工具(如 `perf`)评估缓存命中率变化]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

呼哧呼哧.

栓Q!!!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值