不好意思,list.contain 去重该换换了!

👉 这是一个或许对你有用的社群

🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入「芋道快速开发平台」知识星球。下面是星球提供的部分资料: 

89b4e9ecdeb50212d4f394939f09a194.gif

👉这是一个或许对你有用的开源项目

国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构。

功能涵盖 RBAC 权限、SaaS 多租户、数据权限、商城、支付、工作流、大屏报表、微信公众号等等功能:

  • Boot 地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro

  • Cloud 地址:https://gitee.com/zhijiantianya/yudao-cloud

  • 视频教程:https://doc.iocoder.cn

来源:blog.youkuaiyun.com/qq_35387940
/article/details/129885310


前言

最近又是一轮代码review , 发现了一些实现去重的代码,在使用 lsit.contain ......

如:

a1ac17f2eb2c207a3179fd21ef34f0b7.png

我沉思,是不是其实很多初学者也存在这种去重使用问题?

所以我选择把这个事情整出来,分享一下。

0475401aa2a73028c12fcacbbd6801f8.png

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro

  • 视频教程:https://doc.iocoder.cn/video/

正文

首先是造出一个 List<String> 模拟数据,一共2W条,里面有一半数据1W条是重复的:

public static List<String> getTestList() {
    List<String> list = new ArrayList<>();
    for (int i = 1; i <= 10000; i++) {
        list.add(String.valueOf(i));
    }
    for (int i = 10000; i >= 1; i--) {
        list.add(String.valueOf(i));
    }
    return list;
}

先看看 我们用contain 去重的 代码:

/**
 * 使用 list.contain 去重
 *
 * @param testList
 */
private static void useContain2Distinct(List<String> testList) {
    System.out.println("contains 开始去重,条数:" + testList.size());
    List<String> testListDistinctResult = new ArrayList<>();
    for (String str : testList) {
        if (!testListDistinctResult.contains(str)) {
            testListDistinctResult.add(str);
        }
    }
    System.out.println("contains 去重完毕,条数:" + testListDistinctResult.size());
}

我们调用一下看看耗时:

public static void main(String[] args) {
    List<String> testList = getTestList();
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    useContainDistinct(testList);
    stopWatch.stop();
    System.out.println("去重 最终耗时" + stopWatch.getTotalTimeMillis());
}

耗时:

1c975e587a611b697171e2254a73e228.png

评价:list.contain 的效率,我的建议是,知道就行,别用。

众所周知Set 不存在 重复数据, 所以我们来看看 使用HashSet去重的性能:

ps:这里是采取使用 set的add 方法做去重

/**
 * 使用set去重
 *
 * @param testList
 */
private static void useSetDistinct(List<String> testList) {
    System.out.println("HashSet.add 开始去重,条数:" + testList.size());
    List<String> testListDistinctResult = new ArrayList<>(new HashSet(testList));
    System.out.println("HashSet.add 去重完毕,条数:" + testListDistinctResult.size());
}

我们调用一下看看耗时:

public static void main(String[] args) {
    List<String> testList = getTestList();
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    useSetDistinct(testList);
    stopWatch.stop();
    System.out.println("去重 最终耗时" + stopWatch.getTotalTimeMillis());
}

耗时:

c86b8f0c2f93cd9a8c9ff61905f438f3.png

评价:HashSet 的效率,我的建议是,推荐。

为什么耗时 差距这么大?

不多说,我们看源码:

list.contains(o)

4ab31b61ed0a37257652f4369fb7c732.png

可以看到里面用到了 index(o) :

8efb599dc0c6d1f9ff5b8e0d920e506c.png

时间复杂度 :O(n) ,n: 元素个数

那么我们看看 set.add(o) 是怎么样的 :

5529762205390b59b58b0942fc9d0834.png

map的add , 老生常谈就不谈了,hash完 直接塞到某个位置, 时间复杂度 : O(1)

所以 O(n) 和  O(1) 谁快  谁慢 ?显然。

50349b8af76e089025ffe9bbaa6550ca.png

ps:顺嘴说下 hashset的 contain

30e8aaebe1b3fab33bc054d8e0981ea6.png

时间复杂度也是 :O(1)

f7fc159417f1e4314fe1f49b74ae1bd6.png

那么我们最后再看看别的去重:

双for循环 ,remove去重

/**
 * 使用双for循环去重
 * @param testList
 */
private static void use2ForDistinct(List<String> testList) {
    System.out.println("list 双循环 开始去重,条数:" + testList.size());
    for (int i = 0; i < testList.size(); i++) {
        for (int j = i + 1; j < testList.size(); j++) {
            if (testList.get(i).equals(testList.get(j))) {
                testList.remove(j);
            }
        }
    }
    System.out.println("list 双循环  去重完毕,条数:" + testList.size());
}
public static void main(String[] args) {
    List<String> testList = getTestList();
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    use2ForDistinct(testList);
    stopWatch.stop();
    System.out.println("去重 最终耗时" + stopWatch.getTotalTimeMillis());
}

耗时:

441120748be62919c525aea01abaff5f.png

评价:知道就行,图个乐,别用,贼慢,而且代码看起来乱:。

stream的distinct去重:

/**
 * 使用Stream 去重
 *
 * @param testList
 */
private static void useStreamDistinct(List<String> testList) {
    System.out.println("stream 开始去重,条数:" + testList.size());
    List<String> testListDistinctResult = testList.stream().distinct().collect(Collectors.toList());
    System.out.println("stream 去重完毕,条数:" + testListDistinctResult.size());
}
public static void main(String[] args) {
    List<String> testList = getTestList();
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    useStreamDistinct(testList);
    stopWatch.stop();
    System.out.println("去重 最终耗时" + stopWatch.getTotalTimeMillis());
}

耗时:

d97550a3502454cc71c19b4467bd983b.png

评价:还不错,主要是代码也蛮简洁,有一点点动心。

好了,该篇就到这。


欢迎加入我的知识星球,全面提升技术能力。

👉 加入方式,长按”或“扫描”下方二维码噢

5a1719329606f620d75211fc11c3bcce.png

星球的内容包括:项目实战、面试招聘、源码解析、学习路线。

d0cf2a8d09fe8b7036e8a7dcf852345b.png

ff3980d4e0282b6ff0f7c001788f9b11.png6b8e1f45dfb81498fd6454d26f15ed6d.png17408fec56591df281352ca64c81240e.pngfcb4d6d90027c3e1fe586e190a6c4a70.png

文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)
import type { DrawStyleSecondCategoryGridRef } from '@aiComponents/DrawStyleCategoryGrid/SecondGrid.tsx'; import type { UseDrawStyleCategoryConfigProps } from '@aiComponents/DrawStyleCategoryGrid/useDrawStyleCategoryConfig.ts'; import type { FeatureLeaferFrameLayoutCommonProps, FeatureLeaferFrameLayoutRef, } from '@aiComponents/FeatureLeaferFrameLayout/types.ts'; import type { AiToolGenerateModalRef } from '@aiComponents/GenerateModal'; import type { QueryTagItem } from '@aiComponents/QueryTagImagesSelector/types.ts'; import type { SemanticsSegmentImageControlRef, SemanticsSegmentImageType, } from '@aiComponents/SemanticsSegmentImage/types.ts'; import type { StyleConfigTextAreaRef } from '@aiComponents/StyleConfigTextArea'; import type { UploadCropperModalProps, UploadCropperModalRef, } from '@aiComponents/UploadCropperModal'; import type { AiVipEquityLimitModalRef } from '@aiComponents/VipEquityLimitModal'; import type { ToolEquityCountWrapRef } from '@components/ToolEquityCountWrap'; import type { RequestDecorationDesignParams } from '@services/comfyUI.ts'; import type { RequestJzxzDrawImageParams, RequestTextToImageGenerateParams, } from '@services/draw.ts'; import { aiAddEventTrack, combineSentenceBySplitSymbol, getRandomNum, getStyleDataIndexByRefs, } from '@utils/ai.ts'; import { userAuth } from '@utils/auth.ts'; import { computeContentObjectFit, delayFun } from '@utils/common.ts'; import { decodeImageUrl } from '@utils/string.ts'; import { microGetHuoshanUploadImageUrl } from '@utils/upload.ts'; import type { UploadImageResponse } from '@wander/base-utils'; import { fileToBase64 } from '@wander/base-utils'; import { base64ToBlob, blobToFile, getImageSizeInfo, isMobile } from '@wander/base-utils'; import { useMount } from 'ahooks'; import { message } from 'antd'; import type { AxiosRequestConfig } from 'axios'; import { t } from 'i18next'; import PubSub from 'pubsub-js'; import type { ReactNode } from 'react'; import { useEffect, useRef, useState } from 'react'; import { useLocation, useSearchParams } from 'react-router-dom'; import type { CustomUploadProps } from '@/components/CustomUpload'; import type { UploadBtnImageResponse } from '@/components/HuabanUploadBtn'; import type { ImageSelectorModalProps, ImageSelectorModalRef, } from '@/components/ImageSelectorModal'; import type { ThemeSegmentedProps } from '@/components/ThemeSegmented'; import type { UploadImageType } from '@/components/UploadImageCard'; import { imgToImgSizeConfig, JZXZ_DRAW_INIT_NEGATIVE_PROMPTS } from '@/config/aiTool'; import useGetConfigValue from '@/hooks/useGetConfigValue.ts'; import { DECORATION_DESIGN_CATEGORY_SELECT_CONFIG_KEY, DECORATION_DESIGN_CHARTLET_CATEGORY_KEY, DECORATION_DESIGN_COMFY_UI_SELECT_CATEGORY_CONFIG_KEY, decorationDesignSpeedModePrompts, decorationDesignToolKey, TRACK_DECORATION_DESIGN_TOOL_NAME, } from '@/pages/AiTools/DecorationDesign/config.tsx'; import { commonDrawControlNetModelMap } from '@/pages/AiTools/Draw/configs.ts'; import { useToolRouterStore } from '@/pages/AiTools/Router/store/createStore.ts'; import type { RequestDescribeRetouchingParams } from '@/services/aiTool'; import { requestDescribeRetouching } from '@/services/aiTool'; import { useStore } from '@/store/createStore.ts'; import type { AiToolProcessSuccessResponse, ControlNetEditorImageInfo } from '@/types/ai'; import type { CandidateItem, DrawSizeType, DrawStyleConfigItem, GeneratePriorityType, LoraItem, } from '@/types/draw.ts'; import type { ChartletCategoryItem, ChartletImageItem } from '@/types/image'; import { imageDataToBase64 } from '@/utils/image'; export type VersionValueType = 'STANDARD' | 'EXTREME_SPEED' | 'ADVANCED'; // 请求之间的间隔时长 const REQUEST_DELAY_DURATION = 2000; // 默认试次数 const DEFAULT_RETRY_COUNT = 2; type VersionMobileValue = any; export type VersionOptionItem = { label: ReactNode; value: VersionMobileValue; disabled?: boolean; }; export type CategoriesItem = { key: string; // 风格名称 label: string; // 风格展示图片 thumbUrl: string; // 提示词 promptsList: CandidateItem[]; // 大模型名称 modelName: string; // 小模型 loraParamses?: LoraItem[]; }; export type DecorationDesignStyleItem = { alias: string; key: string; categories: CategoriesItem[]; }; type PromptConfig = { key: string; list: CandidateItem[]; }; export const useAiDecorationDesign = () => { const location = useLocation(); const [searchParams, setSearchParams] = useSearchParams(); const addResultImage = useToolRouterStore((state) => state.addResultImage); // 产品类型 const productType = useStore((state) => state.productInfo.productType); // 不限制权益 const noLimitFrequency = userAuth(['ORGANIZATION_VIP']); // 描述词获取 const styleConfigTextAreaRef = useRef<StyleConfigTextAreaRef>(null); // 底图图库 const imageSelectorModalRef = useRef<ImageSelectorModalRef>(null); // 生成弹窗 const generateModalRef = useRef<AiToolGenerateModalRef<RequestJzxzDrawImageParams>>(null); const drawStyleCategoryRef = useRef<DrawStyleSecondCategoryGridRef>(null); const toolEquityCountWrapRef = useRef<ToolEquityCountWrapRef>(null); // 画板Ref const featureLeaferFrameLayoutRef = useRef<FeatureLeaferFrameLayoutRef>(null); const semanticsSegmentImageControlRef = useRef<SemanticsSegmentImageControlRef>(null); // 刷新次数 const refreshToolEquityUserUseInfo = useStore((state) => state.refreshToolEquityUserUseInfo); // 创作模式优先级 const [generatePriorityType, setGeneratePriorityType] = useState<GeneratePriorityType>('CONTROLNET'); // 会员权益弹窗 const DecorationDesignipLimitModalRef = useRef<AiVipEquityLimitModalRef>(null); // 相似度 const [weight, setWeight] = useState(1); // 最终生成图尺寸大小 const [sizeType, setSizeType] = useState<DrawSizeType>('LARGE'); // 极速版-生成失败的信息 const [errorMsg, setErrorMsg] = useState(''); // 创作弹框-极速版 const generateSpeedModalRef = useRef<AiToolGenerateModalRef>(null); // comfyUI 创作弹框-高级版 const comfyUiGenerateModalRef = useRef<AiToolGenerateModalRef<RequestDecorationDesignParams>>(null); // 风格获取-高级版 const drawComfyUiStyleCategoryRef = useRef<DrawStyleSecondCategoryGridRef>(null); // 风格列表 const [comfyUiStyleList, setComfyUiStyleList] = useState<DecorationDesignStyleItem[]>([]); // 高级版-风格参考图信息 const [styleReferComfyImageInfo, setStyleReferComfyImageInfo] = useState<UploadImageResponse | null>(null); // 风格参考图图库 const comfySourceImageSelectorModalRef = useRef<ImageSelectorModalRef>(null); // comfyUI-XL版本参考图风格符合度 const [comfyStyleReferWeight, setComfyStyleReferWeight] = useState(0.8); // 上传裁剪弹窗 const uploadCropperModalRef = useRef<UploadCropperModalRef>(null); // 裁剪弹窗open const [cropperModalOpen, setCropperModalOpen] = useState(false); // 选中风格索引 const [currentStyleIndex, setCurrentStyleIndex] = useState(0); // 高级版-一级类目状态获取 const [activeAlias, setActiveAlias] = useState<string>(comfyUiStyleList[0]?.alias || ''); // 高级版-获取当前激活的二级类目 const activeCategories = comfyUiStyleList.find((item) => item.alias === activeAlias)?.categories || []; // 版本切换状态 const [versionValue, setVersionValue] = useState<VersionValueType>(() => { return userAuth(['USER', 'COMMON_VIP', 'TOURIST']) ? 'STANDARD' : 'EXTREME_SPEED'; }); // 标准版-该工具支持选择的类目组 const categoryRefsGroup: string[][] = useGetConfigValue({ productKeys: { JZXZ: DECORATION_DESIGN_CATEGORY_SELECT_CONFIG_KEY, JZDS: 'jzds_decoration_design_style_categories', }, }) || []; // 贴图类目列表 const chartletCategoryList = useGetConfigValue<ChartletCategoryItem[]>({ key: DECORATION_DESIGN_CHARTLET_CATEGORY_KEY }) || []; // 线稿库类目列表 const queryTagList = useGetConfigValue<QueryTagItem[]>({ key: 'jzxz_decoration_design_base_Image_query_tag_config', }) || []; // 高级版-该工具支持选择的类目组 const comfyUiCategoryRefsGroup: CategoriesItem[] = ( useGetConfigValue({ productKeys: { JZXZ: DECORATION_DESIGN_COMFY_UI_SELECT_CATEGORY_CONFIG_KEY, }, }) || [] ).filter( (category: CategoriesItem) => category.key === 'indoorHomeDecoration' || category.key === 'indoorWorkWear', ); // 高级版-描述词 const comfyUiPrompt: PromptConfig[] = ( useGetConfigValue({ productKeys: { JZXZ: 'jzxz_advance_draw_descriptor_config', }, }) || [] ).filter((p: PromptConfig) => p.key === 'indoorHomeDecoration' || p.key === 'indoorWorkWear'); console.log('comfyUiPrompt', comfyUiPrompt); // 版本切换-mobile const handleVersionMobileChange = (value: VersionValueType) => { if (userAuth(['TOURIST'])) { PubSub.publish('onLogin', true); return; } if (value !== 'STANDARD' && userAuth(['USER', 'COMMON_VIP'])) { console.log('value', value); DecorationDesignipLimitModalRef.current?.setLimitType('FOREVER_VIP'); return; } // 否则允许切换 setVersionValue(value); // 描述词类型获取 const promptType = styleConfigTextAreaRef.current?.getPromptType(); // 描述词自定义-版本切换判断 if (promptType === 'USER' || promptType === 'INNOVATIVE') { if (value === 'ADVANCED') { styleConfigTextAreaRef.current?.updateCandidateConfig( activeCategories[currentStyleIndex].promptsList, false, ); return; } if (value === 'EXTREME_SPEED') { styleConfigTextAreaRef.current?.updateCandidateConfig( decorationDesignSpeedModePrompts, false, ); return; } if (value === 'STANDARD') { const defaultRefs = categoryRefsGroup[currentStyleIndex]; // 获取对应的类目配置数据 const categories = drawStyleCategoryRef.current?.styleConfigList || []; // 手动触发选择逻辑 const ids = getStyleDataIndexByRefs('id', categories, defaultRefs); if (ids.length) { styleConfigTextAreaRef.current?.updateCandidateConfigByIds(ids, false); } } return; } if (value === 'ADVANCED') { styleConfigTextAreaRef.current?.updateCandidateConfig( activeCategories[currentStyleIndex].promptsList, true, ); return; } if (value === 'EXTREME_SPEED') { styleConfigTextAreaRef.current?.updateCandidateConfig(decorationDesignSpeedModePrompts, true); return; } if (value === 'STANDARD') { const defaultRefs = categoryRefsGroup[currentStyleIndex]; // 获取对应的类目配置数据 const categories = drawStyleCategoryRef.current?.styleConfigList || []; // 手动触发选择逻辑 const ids = getStyleDataIndexByRefs('id', categories, defaultRefs); if (ids.length) { styleConfigTextAreaRef.current?.updateCandidateConfigByIds(ids, true); } } }; // 版本切换-pc const handleVersionPcChange: ThemeSegmentedProps['onChange'] = (item) => { const value = item.value; if (userAuth(['TOURIST'])) { PubSub.publish('onLogin', true); return; } if (value !== 'STANDARD' && userAuth(['USER', 'COMMON_VIP'])) { DecorationDesignipLimitModalRef.current?.setLimitType('FOREVER_VIP'); return; } // 否则允许切换 setVersionValue(value); // 描述词类型获取 const promptType = styleConfigTextAreaRef.current?.getPromptType(); // 描述词自定义-版本切换判断 if (promptType === 'USER' || promptType === 'INNOVATIVE') { if (value === 'ADVANCED') { styleConfigTextAreaRef.current?.updateCandidateConfig( activeCategories[currentStyleIndex].promptsList, false, ); return; } if (value === 'EXTREME_SPEED') { styleConfigTextAreaRef.current?.updateCandidateConfig( decorationDesignSpeedModePrompts, false, ); return; } if (value === 'STANDARD') { const defaultRefs = categoryRefsGroup[currentStyleIndex]; // 获取对应的类目配置数据 const categories = drawStyleCategoryRef.current?.styleConfigList || []; // 手动触发选择逻辑 const ids = getStyleDataIndexByRefs('id', categories, defaultRefs); if (ids.length) { styleConfigTextAreaRef.current?.updateCandidateConfigByIds(ids, false); } } return; } if (value === 'ADVANCED') { styleConfigTextAreaRef.current?.updateCandidateConfig( activeCategories[currentStyleIndex].promptsList, true, ); return; } if (value === 'EXTREME_SPEED') { styleConfigTextAreaRef.current?.updateCandidateConfig(decorationDesignSpeedModePrompts, true); return; } if (value === 'STANDARD') { const defaultRefs = categoryRefsGroup[currentStyleIndex]; // 获取对应的类目配置数据 const categories = drawStyleCategoryRef.current?.styleConfigList || []; // 手动触发选择逻辑 const ids = getStyleDataIndexByRefs('id', categories, defaultRefs); if (ids.length) { styleConfigTextAreaRef.current?.updateCandidateConfigByIds(ids, true); } } }; // 高级版-风格类目描述词对应方法 function mapPromptsToCategories(categoryGroups: any, prompts: any) { return categoryGroups.map((categoryGroup: any) => { const promptConfig = prompts.find((prompt: any) => prompt.key === categoryGroup.key); if (!promptConfig) { return null; } return { alias: categoryGroup.alias, key: categoryGroup.key, categories: categoryGroup.categories.map((cat: any) => ({ key: cat.key, label: cat.alias, thumbUrl: cat.coverUrl, promptsList: promptConfig.list.map((item: any) => ({ placeholder: item.placeholder || '', keyword: item.keyword || '', })), modelName: cat.modelName, lora: cat.loraParamses?.[0], })), }; }); } // 高级版-设计领域与风格切换 const onComfyUiStyleIndexChange = (index: number) => { setCurrentStyleIndex(index); const currentItem = activeCategories[index]; const isUniversalCategory = activeCategories[0]; // 随机描述词 let randomPrompts: CandidateItem[] = []; if (currentItem.promptsList?.length) { const randomIndex = Math.floor(Math.random() * currentItem.promptsList.length); randomPrompts = [currentItem.promptsList[randomIndex]]; } // 更新候选配置 styleConfigTextAreaRef.current?.updateCandidateConfig(randomPrompts, true); if (isUniversalCategory) { styleConfigTextAreaRef.current?.updateCandidateConfig(currentItem.promptsList, true); } }; // 高级版-风格类目名称切换 const handleStyleAliasChange = (item: DecorationDesignStyleItem) => { setActiveAlias(item.alias); // 切换一级类目时置二级类目索引为0 setCurrentStyleIndex(0); // 获取当前激活的二级类目列表 const currentCategories = comfyUiStyleList.find((style) => style.alias === item.alias)?.categories || []; // 确保存在有效的二级类目 if (currentCategories.length === 0) return; // 描述词类型获取 const promptType = styleConfigTextAreaRef.current?.getPromptType(); // 描述词自定义-版本切换判断 if (promptType === 'USER' || promptType === 'INNOVATIVE') { if (versionValue === 'ADVANCED') { styleConfigTextAreaRef.current?.updateCandidateConfig( activeCategories[currentStyleIndex].promptsList, false, ); return; } } // 合并子类的描述词 const allPrompts = currentCategories.flatMap((cat) => cat.promptsList); if (allPrompts.length === 0) return; // 随机选择一个描述词 const randomIndex = Math.floor(Math.random() * allPrompts.length); const randomPrompt = allPrompts[randomIndex]; console.log('randomPrompt', randomPrompt); // 更新到输入框 styleConfigTextAreaRef.current?.updateCandidateConfig([randomPrompt], true); onComfyUiStyleIndexChange(); }; // 获取语意分割组件和局部绘制组件内部参考图信息并转为ControlNet所需参数 const getControlsControlNetArgs = ({ localDrawImage, semanticsSegmentImage, }: { localDrawImage: ControlNetEditorImageInfo | null; semanticsSegmentImage: SemanticsSegmentImageType | null; }) => { const controlNetArgs: Pick< RequestTextToImageGenerateParams, 'localDrawImage' | 'semanticsSegmentImage' > = {}; if (localDrawImage?.enabled) { controlNetArgs.localDrawImage = { ...localDrawImage, ...commonDrawControlNetModelMap.inpaint, enable: true, // 开始迭代步数 guidanceStart: 0, guidanceEnd: 1, // 结束迭代步数 weight: 1, }; } if (semanticsSegmentImage?.enabled) { controlNetArgs.semanticsSegmentImage = { ...semanticsSegmentImage, ...commonDrawControlNetModelMap.semanticsSegment, enable: true, // 开始迭代步数 guidanceStart: 0, guidanceEnd: 1, // 结束迭代步数 weight: 1, }; } return controlNetArgs; }; // 获取画板图片 const getFrameImage = async (sizeInfo: Pick<UploadImageResponse, 'width' | 'height'>) => { // 导出画布方法 const exportFrame = featureLeaferFrameLayoutRef.current?.exportFrame; if (!exportFrame) { return Promise.reject(); } // 导出内容 const result = await exportFrame('canvas', { size: { width: sizeInfo.width, height: sizeInfo.height, }, }); const leaferCanvas = result.data; const context = leaferCanvas.context; // 获取画板图片 const imageData = context.getImageData(0, 0, leaferCanvas.width, leaferCanvas.height); const base64 = imageDataToBase64(imageData); const file = blobToFile(base64ToBlob(base64)); const imageInfo = await microGetHuoshanUploadImageUrl(file); featureLeaferFrameLayoutRef.current?.setCompareImage(imageInfo); return imageInfo; }; // comfyUI-有风格参考图 const generateComfyStyleReferenceTask = async (uploadImage: UploadImageResponse) => { comfyUiGenerateModalRef.current?.setModalOpen(true); let imageInfo: UploadImageResponse = uploadImage; const prompt = styleConfigTextAreaRef.current?.prompts.placeholder || ''; // 当前尺寸的阈值 const thresholdNum = imgToImgSizeConfig[sizeType]; // 尺寸信息 const sizeParams = computeContentObjectFit({ containerWidth: thresholdNum, containerHeight: thresholdNum, contentWidth: uploadImage.width, contentHeight: uploadImage.height, type: 'contain', }); // 风格参考图获取 const baseImage = styleReferComfyImageInfo?.largeUrl; try { if (!featureLeaferFrameLayoutRef.current?.verifyFrameIsEmpty()) { imageInfo = await getFrameImage(uploadImage); } comfyUiGenerateModalRef.current?.generateTask({ imageUrl: imageInfo.largeUrl, seed: getRandomNum(), ...sizeParams, // 描述词 prompt, // 底图符合度 baseImageWeight: weight, // 参考图片链接 styleReferImageUrl: baseImage, // 风格参考度-权 styleReferWeight: comfyStyleReferWeight, }); } catch (e) { comfyUiGenerateModalRef.current?.setRequestStatus('GENERATE_ERROR'); } }; // comfyUI-无风格参考图 const generateComfyTask = async (uploadImage: UploadImageResponse) => { comfyUiGenerateModalRef.current?.setModalOpen(true); let imageInfo: UploadImageResponse = uploadImage; const prompt = styleConfigTextAreaRef.current?.prompts.placeholder || ''; // 当前尺寸的阈值 const thresholdNum = imgToImgSizeConfig[sizeType]; // 尺寸信息 const sizeParams = computeContentObjectFit({ containerWidth: thresholdNum, containerHeight: thresholdNum, contentWidth: uploadImage.width, contentHeight: uploadImage.height, type: 'contain', }); try { if (!featureLeaferFrameLayoutRef.current?.verifyFrameIsEmpty()) { imageInfo = await getFrameImage(uploadImage); } comfyUiGenerateModalRef.current?.generateTask({ imageUrl: imageInfo.largeUrl, seed: getRandomNum(), ...sizeParams, // 描述词 prompt, // 底图符合度 baseImageWeight: weight, }); } catch (e) { comfyUiGenerateModalRef.current?.setRequestStatus('GENERATE_ERROR'); } }; // 标准版-开始创作 const onSdStart: FeatureLeaferFrameLayoutCommonProps['onStart'] = async ({ uploadImage, localDrawImage, semanticsSegmentImage, }) => { if (!noLimitFrequency && !toolEquityCountWrapRef.current?.verifyUseStatus()) { return; } if (!uploadImage) { message.warning(t('AiTools-LocalDrawImage-yES4')); return; } let imageInfo: UploadImageResponse = uploadImage; generateModalRef.current?.setModalOpen(true); try { if (!featureLeaferFrameLayoutRef.current?.verifyFrameIsEmpty()) { imageInfo = await getFrameImage(uploadImage); } // 提取当前类目下下的参数 const { modelName = '', loraParamses = [] } = drawStyleCategoryRef.current?.getCategoryParams() || {}; let prompt = styleConfigTextAreaRef.current?.prompts.keyword || ''; // 启用了色块 获取色块描述 if (semanticsSegmentImage?.enabled) { // 描述词 const paintKeywords = [ ...new Set(semanticsSegmentImage.colorBlockList.map((item) => item.englishAlias)), ]; prompt = combineSentenceBySplitSymbol([...paintKeywords, prompt]); } // 参考图 const styleImage = styleReferComfyImageInfo; generateModalRef.current?.generateTask({ // 获取语意分割、局部绘相关controlnet参数 ...getControlsControlNetArgs({ localDrawImage, semanticsSegmentImage }), prompt, modelName, loraParamses, negativePrompt: JZXZ_DRAW_INIT_NEGATIVE_PROMPTS, baseImage: { width: imageInfo.width, height: imageInfo.height, url: imageInfo.largeUrl || imageInfo.thumbUrl, // 开始迭代步数 guidanceStart: 0, // 结束迭代步数 guidanceEnd: 1, // 相似度 module: 'scribble_xdog', weight, model: 'control_v11p_sd15_scribble', }, ...(styleImage ? { styleReferImage: { guidanceEnd: 1, guidanceStart: 0, height: styleImage.height as number, model: 'ip-adapter_sd15_plus [32cd8f7f]', module: 'ip-adapter_clip_sd15', url: styleImage.largeUrl, weight: comfyStyleReferWeight, width: styleImage.width as number, }, } : {}), sizeType, generateMode: 'REPAINT', generatePriorityType, generateImageType: 'DECORATION_DESIGN', }); } catch (error) { message.error(t('AiTools-DecorationDesign-Operate-hook-trpaP')); } }; // comfy-高级版-开始创作 const onComfyStart: FeatureLeaferFrameLayoutCommonProps['onStart'] = async (params) => { if (!params?.uploadImage) { message.warn(t('AiTools-DetailEnhance-Operate-hook-PzPPn')); return; } if (styleReferComfyImageInfo) { await generateComfyStyleReferenceTask(params.uploadImage); } else { await generateComfyTask(params.uploadImage); } }; // 极速版 const sendRequest = async ( // 请求参数 params: RequestDescribeRetouchingParams, // 试次数 retryCount: number, // 请求头配置 用于减次数 config?: AxiosRequestConfig, ): Promise<UploadImageType> => { // 没有试次数了 直接抛出异常 if (retryCount <= 0) { throw { errorCode: 'MAX_RETRY_TIMES', errorMsg: '生成图片异常' }; } const response = await requestDescribeRetouching(params, config).catch((e) => { console.error('请求失败:', e); return null; }); // 本次生成没有结果 试 if (!response) { await delayFun(REQUEST_DELAY_DURATION); return sendRequest(params, retryCount - 1, config); } // 拿到了结果 正常返回 if (response.imageList[0]) { return response.imageList[0]; } // 拿到了结果但是没有图片返回且有文案提示 if (!response.imageList.length && response.text) { setErrorMsg(response.text.replace(/\*/g, '')); // 特殊错误,不试 throw { errorCode: 'NO_IMAGE', errorMsg: response.text }; } // 其他情况试 await delayFun(REQUEST_DELAY_DURATION); return sendRequest(params, retryCount - 1, config); }; const batchRequest = async (params: RequestDescribeRetouchingParams) => { const result: UploadImageType[] = []; const data = await sendRequest(params, DEFAULT_RETRY_COUNT); result.push(data); return result; }; const onSpeedModeGenerateSuccess = (list: UploadImageType[]) => { const result = list[0].largeUrl; featureLeaferFrameLayoutRef.current?.setResult(result); // 添加进快捷使用 addResultImage({ ...list[0], url: list[0].largeUrl }); aiAddEventTrack(TRACK_DECORATION_DESIGN_TOOL_NAME, 'createSuccess'); refreshToolEquityUserUseInfo(decorationDesignToolKey); }; // 开始创作-极速版 const onSpeedModeStart: FeatureLeaferFrameLayoutCommonProps['onStart'] = async ({ uploadImage, }) => { if (!uploadImage) { message.warning({ content: '请先上传图片', key: 'emptyImage' }); return; } setErrorMsg(''); generateSpeedModalRef.current?.setModalOpen(true); generateSpeedModalRef.current?.setRequestStatus('EXECUTING'); // 底图 const uploadImageInfo = uploadImage.largeUrl; // 参考图 const baseImage = styleReferComfyImageInfo?.largeUrl; const imageUrlList: string[] = []; imageUrlList.push(uploadImageInfo); if (baseImage) { imageUrlList.push(baseImage); } // 描述词 const prompts = styleConfigTextAreaRef.current?.prompts.placeholder || ''; // 拼接描述词-有参考图 const baseImageDescription = `图一参考图二,图片要求按${uploadImage.width}:${uploadImage.height}比例生成`; const newPrompts = `${prompts},${baseImageDescription}`; // 拼接描述词-没有参考图 const noBaseImageDescription = `图片要求按${uploadImage.width}:${uploadImage.height}比例生成`; const promptsText = `${prompts},${noBaseImageDescription}`; // 根据baseImage是否存在,设置description const description = baseImage ? newPrompts : promptsText; try { // 发起请求 const response = await batchRequest({ description: combineSentenceBySplitSymbol([description]), imageUrls: imageUrlList, }); onSpeedModeGenerateSuccess(response); generateSpeedModalRef.current?.setRequestStatus('PENDING'); generateSpeedModalRef.current?.setModalOpen(false); } catch (e) { aiAddEventTrack(TRACK_DECORATION_DESIGN_TOOL_NAME, 'createError'); generateSpeedModalRef.current?.setRequestStatus('POLLING_ERROR'); } }; // 生成结果回调 const onGenerateSuccess = async (response: AiToolProcessSuccessResponse) => { try { const result = response.imageList[0]; // 刷新次数 refreshToolEquityUserUseInfo(decorationDesignToolKey); // 设置显示 featureLeaferFrameLayoutRef.current?.setResult(result); const sizeInfo = await getImageSizeInfo(result); // 添加生成记录 addResultImage({ url: result, ...sizeInfo, }); } catch (e) { console.error(e); } }; // 画板插入贴图 const onChartletItemClick = (imageInfo: ChartletImageItem) => { featureLeaferFrameLayoutRef.current?.insertPicture(imageInfo); }; // 画板插入本地上传图片 const onChartletUploadSuccess: CustomUploadProps['onUploadSuccess'] = async ({ url }) => { try { const size = await getImageSizeInfo(url); featureLeaferFrameLayoutRef.current?.insertPicture({ url, width: size.width, height: size.height, }); } catch (err) { console.error(err); message.error(t('AiTools-ImageEditorModal-hooks-mdQhK')); } }; // 创作优先级改变 const onGeneratePriorityTypeChange = (type: GeneratePriorityType) => { setGeneratePriorityType(type); setWeight(type === 'CONTROLNET' ? 1 : 1.2); }; // 标准版-类目改变回调 const onSelectRefsChange: UseDrawStyleCategoryConfigProps['onSelectRefsChange'] = ( refs, categories, ) => { if (versionValue !== 'STANDARD') { return; } drawStyleCategoryRef.current?.setSelectRefs(refs); const ids = getStyleDataIndexByRefs('id', categories, refs); if (ids.length) { styleConfigTextAreaRef.current?.updateCandidateConfigByIds(ids); } }; // 标准版 const onCategoryInit = (styleConfigItems: DrawStyleConfigItem[]) => { // 一级类目ref const firstCategoryRefs = categoryRefsGroup.map((item) => item[0]); // 过滤出相关的类目信息 const filterStyleConfigItems = styleConfigItems.filter((item) => firstCategoryRefs.includes(item.ref), ); drawStyleCategoryRef.current?.setStyleConfigList(filterStyleConfigItems); // 请求描述词 onSelectRefsChange(categoryRefsGroup[0], filterStyleConfigItems); }; // 当点击继续处理时 const onContinue = async (result: string) => { try { const sizeInfo = await getImageSizeInfo(result); featureLeaferFrameLayoutRef.current?.onUploadSuccess([ { ...sizeInfo, largeUrl: result, thumbUrl: result }, ]); } catch (e) { message.error(t('AiTools-DecorationDesign-Operate-hook-6ipKH')); } }; const onSelectorModalFinish: ImageSelectorModalProps['onFinish'] = async (item) => { featureLeaferFrameLayoutRef.current?.onUploadSuccess([item]); if (item.originFile) { aiAddEventTrack(TRACK_DECORATION_DESIGN_TOOL_NAME, isMobile() ? 'mobileUpload' : 'pcUpload'); } }; // 风格底图选择弹窗回调 const onDecorationDesignReferImageModalFinish: ImageSelectorModalProps['onFinish'] = async ({ largeUrl, thumbUrl, width, height, originFile, }) => { setStyleReferComfyImageInfo({ width, height, largeUrl, thumbUrl, }); let base64; // 原图有originFile直接转base64 if (originFile) { try { base64 = await fileToBase64(originFile); } catch (e) { console.error(e); } } // base64转换成功 打开裁剪弹框 失败则默认为原图 setCropperModalOpen(true); uploadCropperModalRef.current?.setCropperImageUrl(base64 || largeUrl); }; // 风格底图裁剪完成图片弹框回调 const onStyleReferImageCropperSuccess: UploadCropperModalProps['onCropperSuccess'] = async ( obj, ) => { setStyleReferComfyImageInfo(obj); setCropperModalOpen(false); }; const onStyleReferImageCropperError = () => { setCropperModalOpen(false); message.error(t('AiTools-TransferSmudge-Operate-hook-MiGY')); }; // 开始创作版本判断 const generateTask: FeatureLeaferFrameLayoutCommonProps['onStart'] = (params) => { if (userAuth(['TOURIST'])) { PubSub.publish('onLogin', true); return; } if (!noLimitFrequency && !toolEquityCountWrapRef.current?.verifyUseStatus()) { return; } if (versionValue === 'STANDARD') { onSdStart(params); return; } if (versionValue === 'EXTREME_SPEED') { onSpeedModeStart(params); return; } if (versionValue === 'ADVANCED') { onComfyStart(params); } }; useMount(() => { // 获取极速版描述词 styleConfigTextAreaRef.current?.updateCandidateConfig(decorationDesignSpeedModePrompts, true); // 获取高级版风格类目 const comfyUiStyle = mapPromptsToCategories(comfyUiCategoryRefsGroup, comfyUiPrompt); setComfyUiStyleList(comfyUiStyle); if (comfyUiStyle.length > 0) { setActiveAlias(comfyUiStyle[0].alias); setCurrentStyleIndex(0); } const image: UploadBtnImageResponse = location.state?.image; if (image) { featureLeaferFrameLayoutRef.current?.onUploadSuccess([image]); return; } // 地址栏参数传递 const url = decodeImageUrl(searchParams.get('url') || ''); const width = searchParams.get('width'); const height = searchParams.get('height'); if (url && width && height) { featureLeaferFrameLayoutRef.current?.onUploadSuccess([ { largeUrl: url, thumbUrl: url, width: Number(width), height: Number(height), }, ]); setSearchParams({}); } }); // 室内装修初始设置选中室内色块 useEffect(() => { semanticsSegmentImageControlRef.current?.setSelectCategoryIndex(1); }, []); return { noLimitFrequency, styleConfigTextAreaRef, imageSelectorModalRef, generateModalRef, featureLeaferFrameLayoutRef, drawStyleCategoryRef, toolEquityCountWrapRef, semanticsSegmentImageControlRef, generatePriorityType, categoryRefsGroup, weight, setWeight, sizeType, setSizeType, weightSliderMaxSize: generatePriorityType === 'PROMPTS' ? 2 : 1.3, chartletCategoryList, queryTagList, onChartletItemClick, onCategoryInit, onChartletUploadSuccess, onGenerateSuccess, onContinue, onGeneratePriorityTypeChange, onSelectRefsChange, onSelectorModalFinish, versionValue, handleVersionPcChange, productType, comfyUiStyleList, setActiveAlias, activeAlias, activeCategories, onComfyUiStyleIndexChange, currentStyleIndex, comfyUiGenerateModalRef, drawComfyUiStyleCategoryRef, styleReferComfyImageInfo, setStyleReferComfyImageInfo, comfySourceImageSelectorModalRef, comfyStyleReferWeight, setComfyStyleReferWeight, generateTask, onDecorationDesignReferImageModalFinish, uploadCropperModalRef, cropperModalOpen, setCropperModalOpen, onStyleReferImageCropperSuccess, onStyleReferImageCropperError, generateSpeedModalRef, errorMsg, DecorationDesignipLimitModalRef, handleVersionMobileChange, handleStyleAliasChange, }; }; onComfyUiStyleIndexChange();需要传入对应alias的index怎么实现
05-28
<think>嗯,用户的问题是关于如何修改`onComfyUiStyleIndexChange`函数,使其能够根据传入的`alias`对应的`index`来实现功能。我需要仔细分析现有的代码,找出问题所在,并提供解决方案。 首先,我需要理解当前函数的作用。当前的`onComfyUiStyleIndexChange`函数接收一个`index`参数,然后根据这个索引来设置当前激活的二级类目索引。同时,它还会更新描述词的配置。但用户希望传入的是`alias`对应的`index`,这可能意味着用户需要根据某个别名(alias)来找到对应的索引,然后调用这个函数。接下来,我需要查看现有的代码结构。当前的函数可能直接使用`index`来访问`activeCategories`数组。但`activeCategories`是根据`activeAlias`从`comfyUiStyleList`中筛选出来的。因此,如果用户想要根据`alias`来找到对应的二级类目索引,可能需要先在一级类目中找到对应的`alias`,然后在二级类目中找到匹配的项。 可能的步骤是:1.确定当前激活的一级类目(`activeAlias`)。2.在该一级类目下的`categories`数组中,找到与传入的`alias`匹配的项,获取其索引。3.使用这个索引来调用`onComfyUiStyleIndexChange`函数。或者,用户可能希望直接根据`alias`在`activeCategories`中找到对应的索引,而不需要一级类目的信息。这需要检查`activeCategories`中的每个项的`alias`或`label`是否与传入的`alias`匹配。例如,如果`activeCategories`的结构是每个项有`label`属性,那么可以通过遍历数组找到匹配的`label`,然后获取其索引。此外,当前的函数中可能没有处理传入`alias`的逻辑,而是直接使用`index`。因此,需要修改函数,使其能够接受`alias`作为参数,并转换为对应的`index`。 可能的解决方案包括: -修改`onComfyUiStyleIndexChange`函数,使其可以接受`alias`参数,并在内部计算对应的`index`。 -或者,在调用该函数之前,先根据`alias`计算出`index`,然后传入该索引。例如,用户可能在点击某个按钮时传递的是`alias`,而不是`index`,因此需要在事件处理中将`alias`转换为对应的`index`,然后调用函数。 需要检查现有的代码中,`onComfyUiStyleIndexChange`是如何被调用的。例如,在UI组件中,可能是在渲染二级类目时,每个类目项有一个点击事件,传递其`index`。如果现在需要根据`alias`来触发,可能需要先找到该`alias`在`activeCategories`中的位置,然后传递对应的`index`。 或者,如果用户希望直接通过`alias`来调用函数,可以修改函数,使其接收`alias`作为参数,然后在内部查找对应的索引。例如: ```typescriptconstonComfyUiStyleIndexChange =(alias: string) =>{const index= activeCategories.findIndex(cat =>cat.label=== alias);if (index !==-1) {setCurrentStyleIndex(index);//后续逻辑...} };``` 但根据用户的问题描述,可能需要保持函数的参数为`index`,但确保传入的是与当前`activeAlias`对应的二级类目的正确索引。或者,可能当前的实现中,当一级类目切换时,二级类目的索引没有正确置,导致传入的`index`不在有效范围内。例如,当切换一级类目时,`activeCategories`会变化,但`currentStyleIndex`可能仍然指向之前的索引,导致越界。因此,在切换一级类目时,需要将`currentStyleIndex`置为0,这已经在代码中处理了。用户的问题可能出现在如何根据`alias`获取正确的`index`。例如,在渲染二级类目列表时,每个类目项的`onClick`事件应该传递正确的`index`,而这个`index`应该对应于`activeCategories`数组中的位置。 如果用户需要在其他地方根据`alias`来触发索引变更,可能需要先查找该`alias`在`activeCategories`中的索引,然后调用`onComfyUiStyleIndexChange`函数。例如,在某个事件处理中: ```typescriptconsthandleAliasClick= (alias:string)=> {constindex =activeCategories.findIndex(cat=> cat.label ===alias);if(index!== -1){onComfyUiStyleIndexChange(index);} };``` 这样,当用户传入`alias`时,先找到对应的`index`,再调用函数。 总结,解决方案可能包括:1.确保在调用`onComfyUiStyleIndexChange`时传入正确的`index`,该`index`对应于当前`activeCategories`数组中的位置。2.如果需要根据`alias`获取`index`,在调用函数前进行转换。3.修改函数本身,使其能够处理`alias`参数,并内部计算`index`。根据用户提供的代码,当前的`onComfyUiStyleIndexChange`函数接收`index`作为参数,并更新状态。因此,正确的做法是在调用该函数时,确保传入的`index`是正确的,对应于当前`activeCategories`中的项。例如,在渲染二级类目时,遍历`activeCategories`数组,并为每个项绑定点击事件,传递其索引: ```typescriptactiveCategories.map((category, index) =>(<ButtononClick={()=> onComfyUiStyleIndexChange(index)}>{category.label}</Button>)) ```这样,当用户点击某个类目时,传入的`index`就是正确的。如果用户的问题是由于在切换一级类目后,`currentStyleIndex`没有正确置,导致传入的`index`超出范围,那么需要确保在切换一级类目时,置`currentStyleIndex`为0,这已经在代码中处理:```typescriptconst handleStyleAliasChange= (item:DecorationDesignStyleItem)=> {setActiveAlias(item.alias);//切换一级类目时置二级类目索引为0setCurrentStyleIndex(0);// ...其他逻辑}; ```因此,问题可能出在如何正确传递`index`给`onComfyUiStyleIndexChange`函数。解决方案是确保在调用该函数时,传入的`index`是当前`activeCategories`数组中的有效索引。如果需要根据`alias`来获取`index`,可以在调用函数前进行查找:```typescriptconst alias= '某个别名'; constindex =activeCategories.findIndex(cat=> cat.label ===alias);if (index !==-1) {onComfyUiStyleIndexChange(index);} ```然后调用函数时传入该`index`。综上,正确的实现方式是在调用`onComfyUiStyleIndexChange`时,传入对应`alias`在`activeCategories`中的索引,这可以通过在调用前使用`findIndex`方法获取。</think>要实现根据`alias`对应的`index`调用`onComfyUiStyleIndexChange`,可以通过以下方式实现: ```typescript // 修改后的函数实现 const onComfyUiStyleIndexChange = (index: number) => { // 确保索引在有效范围内 if (index < 0 || index >= activeCategories.length) return; setCurrentStyleIndex(index); const currentItem = activeCategories[index]; // 更新候选描述词配置 styleConfigTextAreaRef.current?.updateCandidateConfig( currentItem.promptsList || [], true ); }; // 调用示例(根据alias获取index) const targetAlias = "需要定位的类目名称"; const targetIndex = activeCategories.findIndex( cat => cat.label === targetAlias ); if (targetIndex !== -1) { onComfyUiStyleIndexChange(targetIndex); } ``` **实现解释:** 1. **有效性检查**:在函数开头添加索引范围检查,防止无效索引 2. **动态更新配置**:根据当前选中的类目动态更新提示词配置 3. **查找逻辑**:通过`Array.findIndex()`根据alias查找对应索引 4. **错误处理**:当找不到对应alias时自动跳过执行 **组件调用示例:** ```tsx {activeCategories.map((category, index) => ( <Button key={category.key} onClick={() => onComfyUiStyleIndexChange(index)} > {category.label} </Button> ))} ``` **相关参数说明:** - `activeCategories`:当前激活的二级类目列表 - `currentStyleIndex`:状态变量存储当前选中索引 - `setCurrentStyleIndex`:状态更新方法 - `styleConfigTextAreaRef`:描述词输入框的引用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值