@synapse_people

Как сделать ID для объектов?

Есть необходимость пронумеровать(выдать ID) некоторые объекты, например, так: long id = IdService.getId(obj);
Есть идея складывать все объекты в List, затем возвращать индекс объекта в листе, но тут сразу вопрос по поводу GC... ведь он их не удалит и память будет занята...
Как быть? П.с. объекты могут быть из JDK, а могут из пользовательского кода, нужен вариант без модификации кода, а также без использования hashCode,equals(т.к. могут быть неверно переопределены,например в util.Set), а также без System.identity*-т.к. там нет гарантии, что ID уникальный.
  • Вопрос задан
  • 1865 просмотров
Решения вопроса 1
@nubus4000
Можно использовать такой говнокод. Смысл - создавать класс на лету, если у него нет поля id или его нельзя нормально сравнить по equals и hashcode. Код надо подкрутить, он может не сработать. Писал на лету так сказать.

import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.atomic.*;

public class SimpleStorage {

    private Map<Integer, Object> objects = new HashMap<>();
    private AtomicInteger idGenerator = new AtomicInteger(0);
    private ClassMaker classMaker = new ClassMaker(0, null, null);

    private static final String[] EQUALS_NOT_SUPPORTED = {}; //указать имя классов, которые не поддерживают equals и hasCode

    public Integer getId(Object obj) {
        Object finalObj = obj;
        boolean isEqualsNotSupported = Arrays.stream(EQUALS_NOT_SUPPORTED)
                .anyMatch(typeName -> finalObj.getClass().getCanonicalName().equalsIgnoreCase(typeName));

        if (isEqualsNotSupported) {
            final Integer id = getIdFromField(obj);
            return objects.entrySet()
                    .stream()
                    .filter(entry -> id.equals(entry.getValue()))
                    .map(Map.Entry::getKey)
                    .findFirst().get();
        }

        Object finalObj1 = obj;
        return objects.entrySet()
                .stream()
                .filter(entry -> finalObj1.equals(entry.getValue()))
                .map(Map.Entry::getKey)
                .findFirst().get();
    }

    public void add(Object obj) {
        Integer newId = idGenerator.incrementAndGet();
        obj = classMaker.createClassWithId(newId);
        objects.put(newId, obj);
    }

    public Object get(Integer id) {
        return objects.get(id);
    }

    private Integer getIdFromField(Object obj) {
        try {
            Field field = obj.getClass().getField(ClassMaker.FIELD_ID);
            field.setAccessible(true);
            return (Integer) field.get(obj);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            return null;
        }
    }

}


import java.io.*;
import java.net.*;
import java.util.*;
import javax.tools.*;

public class ClassMaker {

    public static final String FIELD_ID = "id";

    private Integer id = 0;
    private String className;
    private String sourceCode;
    private File sourceFile;

    public ClassMaker(Integer id, String className, String sourceCode) {
        this.id = id;
        this.className = className;
        this.sourceCode = sourceCode;
    }

    public Object createClassWithId(Integer id) {
        if (id != null)
            this.id = id;

        sourceCode = createSimpleString(id);
        Object obj = null;
        try (FileWriter writer = new FileWriter(createTmpFile())) {
            writer.write(sourceCode);
            compileClass();
            className = sourceFile.getName().split("\\.")[0];
            URLClassLoader classLoader = URLClassLoader.newInstance(new URL[]{sourceFile.getParentFile().toURI().toURL()});
            Class<?> newClass = classLoader.loadClass(className);
            obj = newClass.newInstance();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }
        return obj;
    }

    private void compileClass() throws IOException {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
        File parentDirectory = sourceFile.getParentFile();
        fileManager.setLocation(StandardLocation.CLASS_OUTPUT, Collections.singletonList(parentDirectory));
        Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromFiles(Collections.singletonList(sourceFile));
        compiler.getTask(null, fileManager, null, null, null, compilationUnits).call();
        fileManager.close();
    }

    private File createTmpFile() throws IOException {
        File sourceFile = File.createTempFile(className, ".java");
        sourceFile.deleteOnExit();
        this.sourceFile = sourceFile;
        return sourceFile;
    }

    private String createSimpleString(Integer id) {
        StringBuilder classBuilder = new StringBuilder();
        classBuilder.append("public class ")
                .append(className)
                .append(" {")
                .append("private Integer id = ")
                .append(id)
                .append(";")
                .append("    public void setId(Integer id) {\n")
                .append("        this.id = id;\n").append("    }\n")
                .append("\n").append("    public Integer getId() {\n")
                .append("        return id;\n")
                .append("    }").append("    @Override\n")
                .append("    public boolean equals(Object o) {\n")
                .append("        if (this == o) return true;\n")
                .append("        if (o == null || getClass() != o.getClass()) return false;\n")
                .append("\n")
                .append(className)
                .append(" that = ")
                .append("(").append(className).append(")").append(" o;")
                .append("\n")
                .append("        return id != null ? id.equals(that.id) : that.id == null;\n")
                .append("    }\n").append("\n").append("    @Override\n")
                .append("    public int hashCode() {\n")
                .append("        return id != null ? id.hashCode() : 0;\n")
                .append("    }\n")
                .append("}");
        return classBuilder.toString();
    }
}


А вообще странная задача - давать всем уникальные id. Откуда объекты берутся? Почему нельзя инкапсулировать всякие сеты и листы внутри пользовательских классов?
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
@frf_nn
человек с компом
* что бы GC удалил, можно использовать WeakReference
* для ID можно использовать UUID
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы