【问题标题】:Is there a way to chain Lists (or Maps) in a for loop?有没有办法在 for 循环中链接列表(或地图)?
【发布时间】:2014-02-14 22:29:51
【问题描述】:

拥有此代码:

Map<String,Rect> map1 = new HashMap<String,Rect>();
Map<String,Rect> map2 = new HashMap<String,Rect>();
...
for( Rect r: map1.values() ) {
    r.offset(10,10);
}
for( Rect r: map2.values() ) {
    r.offset(10,10);
}

如果两个循环中的代码相同,有没有一种简单的方法可以将两个循环组合成一个循环? (假设所有地图键都不同)。
我确实有一些想法也会有所帮助(比如将两张地图合并到一张地图中,或者有一个方法以地图作为参数进行循环),但我想知道如果 Java 语法中有一些东西只允许链接两个相似的列表并循环它们。

【问题讨论】:

标签: java loops for-loop


【解决方案1】:

Java 本身并不支持这种行为。

如果您要使用Google Guava,那么您可以这样做:

for (final Rect rect : Iterables.concat(map1.values(), map2.values())) {

}

您还可以创建一个快速实用程序类。我在这里使用了可变参数,以便更好地使用;缺点是编译器会抛出大量警告,因为泛型数组创建和可变参数方法未选中。

public class CombinedIterable<T> implements Iterable<T> {

    public static <T> Iterable<T> concat(Iterable<? extends T>... iterables) {
        return new CombinedIterable<T>(iterables);
    }

    private final List<Iterable<? extends T>> iterables;

    private CombinedIterable(Iterable<? extends T>... iterables) {
        this.iterables = Arrays.asList(iterables);
    }

    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {

            private final Iterator<Iterable<? extends T>> pos = iterables.iterator();
            private Iterator<? extends T> iter = pos.next().iterator();

            @Override
            public boolean hasNext() {
                if (iter.hasNext()) {
                    return true;
                }
                if (pos.hasNext()) {
                    iter = pos.next().iterator();
                }
                return iter.hasNext();
            }

            @Override
            public T next() {
                return iter.next();
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException("Not supported.");
            }
        };
    }

}

用法类似于:

final List<String> s1 = Arrays.asList(new String[]{"A", "B", "C"});
final List<String> s2 = Arrays.asList(new String[]{"D", "E", "F"});
final List<String> s3 = Arrays.asList(new String[]{"H", "I", "J"});
for (final String s : CombinedIterable.concat(s1, s2, s3)) {
    System.out.println(s);
}

还有import static的魔力

for (final String s : concat(s1, s2, s3)) {
    System.out.println(s);
}

如果你喜欢这种事情......

【讨论】:

  • CombinedIterable.concat(s1,s2,s3) 不应该是CombinedIterable&lt;String&gt;.concat(s1,s2,s3) 吗?
  • @ilomambo;不。我使用了静态工厂方法模式来避免指定类型。
  • 为什么不支持remove()方法呢?执行iter.remove()而不是抛出异常有什么问题?
  • @ilomambo 没问题,我只是没有。根据您的用例,您可能不想允许从串联的Iterables 中删除。如果列表中的一些 Iterables 支持 remove 而有些不支持,那么行为也会有些奇怪。
  • 最后的澄清。如果我没记错的话CombinedIterable 允许concat 也属于不同类的可迭代对象,例如 Map 和 Set。
【解决方案2】:

Crate 方法并根据需要多次调用它

public void updateOffset(Map<String,Rect> map) {
    for( Rect r: map.values() ) {
        r.offset(10,10);
    }
}

现在你的代码变成了

Map<String,Rect> map1 = new HashMap<String,Rect>();
Map<String,Rect> map2 = new HashMap<String,Rect>();
...
updateOffset(map1);
updateOffset(map2);

地图的组合总是限制你的地图总是有唯一的键。因此,仅当您确定您的密钥始终是唯一的时才进行合并。

【讨论】:

  • 谢谢你,Ashish,这也是我在问题中提出的想法之一。我其实想知道java中是否有一些列表链接语法。
  • @ilomambo 没有 eact 列表链接方法。但是,如果您不想这样做,您可以将所有值添加到 List/Set,然后在单次迭代中执行操作
  • 再次感谢 Ashish,这也是我提出的另一个想法(合并)。我担心迭代器的本质可能会阻止列表链接,并且必须使用其他两种解决方案之一。只是认为使用链接代码看起来会更优雅。让我们等几个小时看看是否有人有一些新信息,如果没有我会接受你的回答。
猜你喜欢
  • 2021-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-30
  • 2018-03-19
  • 2023-03-04
相关资源
最近更新 更多