有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java更改方法内的字符串,该字符串应位于方法外

首先,我想展示我的代码:

public static void main(String[] args)
    {
        Map<String,Integer> map = new LinkedHashMap<String,Integer>();
        map.put("bye",0);
        map.put("Hello",1);
        
        System.out.println(Arrays.toString(map(map, new BiConsumer<String,Integer>(){
            @Override
            public void accept(String s, Integer i){
                s=s+"s";
            }
        })));
    }
    
    public static String[] map(Map<String,Integer> m, BiConsumer<String,Integer> bif){
        String[] key = m.keySet().toArray(new String[0]);
        String[] entries = new String[m.size()];
        for (int i = 0; i < m.size(); i++) {
            bif.accept(key[i],m.get(key[i]));
            entries[i] = key[i] + ", " + m.get(key[i]);
        }
        return entries;
    }

如您所见,我想开发一种接受映射的方法<;字符串,整数>;和双消费者<;字符串,整数>;,这就是传递函数对映射中的每个键和值所要做的,并最终返回一个字符串数组

有人可能会说:一种大致上与forEach相同的方法: https://docs.oracle.com/javase/8/docs/api/java/util/LinkedHashMap.html#forEach-java.util.function.BiConsumer-

我能够添加BiConsumer接口,因为它不依赖于其他Java8类

我需要在不改变以下代码中任何内容的情况下开发此方法,并且仅使用Java 7

System.out.println(Arrays.toString(map(map, new BiConsumer<String,Integer>(){
            @Override
            public void accept(String s, Integer i){
                s=s+"s";
            }
        })));

预期结果:

[byes, 0, Hellos, 1]

但我得到了:

[bye, 0, Hello, 1]

因此,我不知道是否应该在方法外部的方法中更改字符串

你有什么想法吗


共 (2) 个答案

  1. # 1 楼答案

    不幸的是,正如您可能经历的那样,Java字符串是不可变的。这意味着,每当您编写类似s = s + s;的内容时,您都在创建一个新对象。此外,Java总是按值传递。因此,任何对象引用(如accept方法中的字符串)也会按值传递。这意味着做一些事情,比如-

    void method(String s){
        s = new String();
    }
    

    也不会更改方法外部的引用

    有一些方法可以解决这个问题,但这有点麻烦。实际上,您将字符串引用存储在一个新类中,然后对该类进行操作。下面是一个演示:

    public class StringRefContainer{
        public String str;
        StringRefContainer(String str){this.str = str;}
    }
    
    // example accept method
    void accept(StringRefContainer s, Integer i){
        // operate directly on s.str
        // eg s = s + 's'
        s.str = s.str + "s";
    }
    

    显然,您可以根据您的用例对此进行改进(例如,为StringRefContainer类重写toString、hashcode、equals等,或者更改/添加访问说明符以适合您的设计,等等)

  2. # 2 楼答案

    这一行:

    s=s+"s";
    

    创建一个新的String并将其存储在s。在赋值之前,skeys[i]存储对同一对象的引用。赋值之后,因为s引用了一个新对象,与keys[i]引用的对象完全无关。事实上,您不能通过更改s来更改keys[i]中的字符串,即使s在方法的开头引用了与keys[i]相同的对象。这是因为String是不可变的。另见:Immutability of Strings in Java

    可以做的是让BiConsumer返回创建的新字符串,并在调用者一侧,将返回的字符串分配给keys[i]。在这一点上,我们不再有一个BiConsumer<String, Integer>,而是一个BiFunction<String, Integer, String>

    interface BiFunction<T, U, R> {
        R apply(T t, U u);
    }
    

    map看起来像这样:

    public static String[] map(Map<String,Integer> m, BiFunction<String,Integer, String> bif){
        String[] key = m.keySet().toArray(new String[0]);
        String[] entries = new String[m.size()];
        for (int i = 0; i < m.size(); i++) {
            Integer value = m.get(key[i]);
            String newKey = bif.apply(key[i], value); // note here!
            entries[i] = newKey + ", " + value;
    
            // uncomment if you want the map's keys to also change
            /*
            m.remove(key[i]);
            m.put(newKey, value);
            */
        }
        return entries;
    }
    

    你会这样称呼它:

    map(map, new BiFunction<>(){
            @Override
            public void apply(String t, Integer u){
                return t + "s";
            }
        })